1##@file scip.pyx
2#@brief holding functions in python that reference the SCIP public functions included in scip.pxd
3import weakref
4from os.path import abspath
5from os.path import splitext
6import sys
7import warnings
8
9cimport cython
10from cpython cimport Py_INCREF, Py_DECREF
11from cpython.pycapsule cimport PyCapsule_New, PyCapsule_IsValid, PyCapsule_GetPointer
12from libc.stdlib cimport malloc, free
13from libc.stdio cimport fdopen
14
15include "expr.pxi"
16include "lp.pxi"
17include "benders.pxi"
18include "benderscut.pxi"
19include "branchrule.pxi"
20include "conshdlr.pxi"
21include "event.pxi"
22include "heuristic.pxi"
23include "presol.pxi"
24include "pricer.pxi"
25include "propagator.pxi"
26include "sepa.pxi"
27include "relax.pxi"
28include "nodesel.pxi"
29
30# recommended SCIP version; major version is required
31MAJOR = 7
32MINOR = 0
33PATCH = 1
34
35# for external user functions use def; for functions used only inside the interface (starting with _) use cdef
36# todo: check whether this is currently done like this
37
38if sys.version_info >= (3, 0):
39    str_conversion = lambda x:bytes(x,'utf-8')
40else:
41    str_conversion = lambda x:x
42
43_SCIP_BOUNDTYPE_TO_STRING = {SCIP_BOUNDTYPE_UPPER: '<=',
44                             SCIP_BOUNDTYPE_LOWER: '>='}
45
46# Mapping the SCIP_RESULT enum to a python class
47# This is required to return SCIP_RESULT in the python code
48# In __init__.py this is imported as SCIP_RESULT to keep the
49# original naming scheme using capital letters
50cdef class PY_SCIP_RESULT:
51    DIDNOTRUN   = SCIP_DIDNOTRUN
52    DELAYED     = SCIP_DELAYED
53    DIDNOTFIND  = SCIP_DIDNOTFIND
54    FEASIBLE    = SCIP_FEASIBLE
55    INFEASIBLE  = SCIP_INFEASIBLE
56    UNBOUNDED   = SCIP_UNBOUNDED
57    CUTOFF      = SCIP_CUTOFF
58    SEPARATED   = SCIP_SEPARATED
59    NEWROUND    = SCIP_NEWROUND
60    REDUCEDDOM  = SCIP_REDUCEDDOM
61    CONSADDED   = SCIP_CONSADDED
62    CONSCHANGED = SCIP_CONSCHANGED
63    BRANCHED    = SCIP_BRANCHED
64    SOLVELP     = SCIP_SOLVELP
65    FOUNDSOL    = SCIP_FOUNDSOL
66    SUSPENDED   = SCIP_SUSPENDED
67    SUCCESS     = SCIP_SUCCESS
68
69cdef class PY_SCIP_PARAMSETTING:
70    DEFAULT     = SCIP_PARAMSETTING_DEFAULT
71    AGGRESSIVE  = SCIP_PARAMSETTING_AGGRESSIVE
72    FAST        = SCIP_PARAMSETTING_FAST
73    OFF         = SCIP_PARAMSETTING_OFF
74
75cdef class PY_SCIP_PARAMEMPHASIS:
76    DEFAULT      = SCIP_PARAMEMPHASIS_DEFAULT
77    CPSOLVER     = SCIP_PARAMEMPHASIS_CPSOLVER
78    EASYCIP      = SCIP_PARAMEMPHASIS_EASYCIP
79    FEASIBILITY  = SCIP_PARAMEMPHASIS_FEASIBILITY
80    HARDLP       = SCIP_PARAMEMPHASIS_HARDLP
81    OPTIMALITY   = SCIP_PARAMEMPHASIS_OPTIMALITY
82    COUNTER      = SCIP_PARAMEMPHASIS_COUNTER
83    PHASEFEAS    = SCIP_PARAMEMPHASIS_PHASEFEAS
84    PHASEIMPROVE = SCIP_PARAMEMPHASIS_PHASEIMPROVE
85    PHASEPROOF   = SCIP_PARAMEMPHASIS_PHASEPROOF
86
87cdef class PY_SCIP_STATUS:
88    UNKNOWN        = SCIP_STATUS_UNKNOWN
89    USERINTERRUPT  = SCIP_STATUS_USERINTERRUPT
90    NODELIMIT      = SCIP_STATUS_NODELIMIT
91    TOTALNODELIMIT = SCIP_STATUS_TOTALNODELIMIT
92    STALLNODELIMIT = SCIP_STATUS_STALLNODELIMIT
93    TIMELIMIT      = SCIP_STATUS_TIMELIMIT
94    MEMLIMIT       = SCIP_STATUS_MEMLIMIT
95    GAPLIMIT       = SCIP_STATUS_GAPLIMIT
96    SOLLIMIT       = SCIP_STATUS_SOLLIMIT
97    BESTSOLLIMIT   = SCIP_STATUS_BESTSOLLIMIT
98    RESTARTLIMIT   = SCIP_STATUS_RESTARTLIMIT
99    OPTIMAL        = SCIP_STATUS_OPTIMAL
100    INFEASIBLE     = SCIP_STATUS_INFEASIBLE
101    UNBOUNDED      = SCIP_STATUS_UNBOUNDED
102    INFORUNBD      = SCIP_STATUS_INFORUNBD
103
104cdef class PY_SCIP_STAGE:
105    INIT         = SCIP_STAGE_INIT
106    PROBLEM      = SCIP_STAGE_PROBLEM
107    TRANSFORMING = SCIP_STAGE_TRANSFORMING
108    TRANSFORMED  = SCIP_STAGE_TRANSFORMED
109    INITPRESOLVE = SCIP_STAGE_INITPRESOLVE
110    PRESOLVING   = SCIP_STAGE_PRESOLVING
111    EXITPRESOLVE = SCIP_STAGE_EXITPRESOLVE
112    PRESOLVED    = SCIP_STAGE_PRESOLVED
113    INITSOLVE    = SCIP_STAGE_INITSOLVE
114    SOLVING      = SCIP_STAGE_SOLVING
115    SOLVED       = SCIP_STAGE_SOLVED
116    EXITSOLVE    = SCIP_STAGE_EXITSOLVE
117    FREETRANS    = SCIP_STAGE_FREETRANS
118    FREE         = SCIP_STAGE_FREE
119
120cdef class PY_SCIP_NODETYPE:
121    FOCUSNODE   = SCIP_NODETYPE_FOCUSNODE
122    PROBINGNODE = SCIP_NODETYPE_PROBINGNODE
123    SIBLING     = SCIP_NODETYPE_SIBLING
124    CHILD       = SCIP_NODETYPE_CHILD
125    LEAF        = SCIP_NODETYPE_LEAF
126    DEADEND     = SCIP_NODETYPE_DEADEND
127    JUNCTION    = SCIP_NODETYPE_JUNCTION
128    PSEUDOFORK  = SCIP_NODETYPE_PSEUDOFORK
129    FORK        = SCIP_NODETYPE_FORK
130    SUBROOT     = SCIP_NODETYPE_SUBROOT
131    REFOCUSNODE = SCIP_NODETYPE_REFOCUSNODE
132
133
134cdef class PY_SCIP_PROPTIMING:
135    BEFORELP     = SCIP_PROPTIMING_BEFORELP
136    DURINGLPLOOP = SCIP_PROPTIMING_DURINGLPLOOP
137    AFTERLPLOOP  = SCIP_PROPTIMING_AFTERLPLOOP
138    AFTERLPNODE  = SCIP_PROPTIMING_AFTERLPNODE
139
140cdef class PY_SCIP_PRESOLTIMING:
141    NONE       = SCIP_PRESOLTIMING_NONE
142    FAST       = SCIP_PRESOLTIMING_FAST
143    MEDIUM     = SCIP_PRESOLTIMING_MEDIUM
144    EXHAUSTIVE = SCIP_PRESOLTIMING_EXHAUSTIVE
145
146cdef class PY_SCIP_HEURTIMING:
147    BEFORENODE        = SCIP_HEURTIMING_BEFORENODE
148    DURINGLPLOOP      = SCIP_HEURTIMING_DURINGLPLOOP
149    AFTERLPLOOP       = SCIP_HEURTIMING_AFTERLPLOOP
150    AFTERLPNODE       = SCIP_HEURTIMING_AFTERLPNODE
151    AFTERPSEUDONODE   = SCIP_HEURTIMING_AFTERPSEUDONODE
152    AFTERLPPLUNGE     = SCIP_HEURTIMING_AFTERLPPLUNGE
153    AFTERPSEUDOPLUNGE = SCIP_HEURTIMING_AFTERPSEUDOPLUNGE
154    DURINGPRICINGLOOP = SCIP_HEURTIMING_DURINGPRICINGLOOP
155    BEFOREPRESOL      = SCIP_HEURTIMING_BEFOREPRESOL
156    DURINGPRESOLLOOP  = SCIP_HEURTIMING_DURINGPRESOLLOOP
157    AFTERPROPLOOP     = SCIP_HEURTIMING_AFTERPROPLOOP
158
159cdef class PY_SCIP_EVENTTYPE:
160    DISABLED        = SCIP_EVENTTYPE_DISABLED
161    VARADDED        = SCIP_EVENTTYPE_VARADDED
162    VARDELETED      = SCIP_EVENTTYPE_VARDELETED
163    VARFIXED        = SCIP_EVENTTYPE_VARFIXED
164    VARUNLOCKED     = SCIP_EVENTTYPE_VARUNLOCKED
165    OBJCHANGED      = SCIP_EVENTTYPE_OBJCHANGED
166    GLBCHANGED      = SCIP_EVENTTYPE_GLBCHANGED
167    GUBCHANGED      = SCIP_EVENTTYPE_GUBCHANGED
168    LBTIGHTENED     = SCIP_EVENTTYPE_LBTIGHTENED
169    LBRELAXED       = SCIP_EVENTTYPE_LBRELAXED
170    UBTIGHTENED     = SCIP_EVENTTYPE_UBTIGHTENED
171    UBRELAXED       = SCIP_EVENTTYPE_UBRELAXED
172    GHOLEADDED      = SCIP_EVENTTYPE_GHOLEADDED
173    GHOLEREMOVED    = SCIP_EVENTTYPE_GHOLEREMOVED
174    LHOLEADDED      = SCIP_EVENTTYPE_LHOLEADDED
175    LHOLEREMOVED    = SCIP_EVENTTYPE_LHOLEREMOVED
176    IMPLADDED       = SCIP_EVENTTYPE_IMPLADDED
177    PRESOLVEROUND   = SCIP_EVENTTYPE_PRESOLVEROUND
178    NODEFOCUSED     = SCIP_EVENTTYPE_NODEFOCUSED
179    NODEFEASIBLE    = SCIP_EVENTTYPE_NODEFEASIBLE
180    NODEINFEASIBLE  = SCIP_EVENTTYPE_NODEINFEASIBLE
181    NODEBRANCHED    = SCIP_EVENTTYPE_NODEBRANCHED
182    FIRSTLPSOLVED   = SCIP_EVENTTYPE_FIRSTLPSOLVED
183    LPSOLVED        = SCIP_EVENTTYPE_LPSOLVED
184    LPEVENT         = SCIP_EVENTTYPE_LPEVENT
185    POORSOLFOUND    = SCIP_EVENTTYPE_POORSOLFOUND
186    BESTSOLFOUND    = SCIP_EVENTTYPE_BESTSOLFOUND
187    ROWADDEDSEPA    = SCIP_EVENTTYPE_ROWADDEDSEPA
188    ROWDELETEDSEPA  = SCIP_EVENTTYPE_ROWDELETEDSEPA
189    ROWADDEDLP      = SCIP_EVENTTYPE_ROWADDEDLP
190    ROWDELETEDLP    = SCIP_EVENTTYPE_ROWDELETEDLP
191    ROWCOEFCHANGED  = SCIP_EVENTTYPE_ROWCOEFCHANGED
192    ROWCONSTCHANGED = SCIP_EVENTTYPE_ROWCONSTCHANGED
193    ROWSIDECHANGED  = SCIP_EVENTTYPE_ROWSIDECHANGED
194    SYNC            = SCIP_EVENTTYPE_SYNC
195    NODESOLVED      = SCIP_EVENTTYPE_NODEFEASIBLE | SCIP_EVENTTYPE_NODEINFEASIBLE | SCIP_EVENTTYPE_NODEBRANCHED
196
197cdef class PY_SCIP_LPSOLSTAT:
198    NOTSOLVED    = SCIP_LPSOLSTAT_NOTSOLVED
199    OPTIMAL      = SCIP_LPSOLSTAT_OPTIMAL
200    INFEASIBLE   = SCIP_LPSOLSTAT_INFEASIBLE
201    UNBOUNDEDRAY = SCIP_LPSOLSTAT_UNBOUNDEDRAY
202    OBJLIMIT     = SCIP_LPSOLSTAT_OBJLIMIT
203    ITERLIMIT    = SCIP_LPSOLSTAT_ITERLIMIT
204    TIMELIMIT    = SCIP_LPSOLSTAT_TIMELIMIT
205    ERROR        = SCIP_LPSOLSTAT_ERROR
206
207cdef class PY_SCIP_BRANCHDIR:
208    DOWNWARDS = SCIP_BRANCHDIR_DOWNWARDS
209    UPWARDS   = SCIP_BRANCHDIR_UPWARDS
210    FIXED     = SCIP_BRANCHDIR_FIXED
211    AUTO      = SCIP_BRANCHDIR_AUTO
212
213cdef class PY_SCIP_BENDERSENFOTYPE:
214    LP     = SCIP_BENDERSENFOTYPE_LP
215    RELAX  = SCIP_BENDERSENFOTYPE_RELAX
216    PSEUDO = SCIP_BENDERSENFOTYPE_PSEUDO
217    CHECK  = SCIP_BENDERSENFOTYPE_CHECK
218
219cdef class PY_SCIP_ROWORIGINTYPE:
220    UNSPEC = SCIP_ROWORIGINTYPE_UNSPEC
221    CONS   = SCIP_ROWORIGINTYPE_CONS
222    SEPA   = SCIP_ROWORIGINTYPE_SEPA
223    REOPT  = SCIP_ROWORIGINTYPE_REOPT
224
225def PY_SCIP_CALL(SCIP_RETCODE rc):
226    if rc == SCIP_OKAY:
227        pass
228    elif rc == SCIP_ERROR:
229        raise Exception('SCIP: unspecified error!')
230    elif rc == SCIP_NOMEMORY:
231        raise MemoryError('SCIP: insufficient memory error!')
232    elif rc == SCIP_READERROR:
233        raise IOError('SCIP: read error!')
234    elif rc == SCIP_WRITEERROR:
235        raise IOError('SCIP: write error!')
236    elif rc == SCIP_NOFILE:
237        raise IOError('SCIP: file not found error!')
238    elif rc == SCIP_FILECREATEERROR:
239        raise IOError('SCIP: cannot create file!')
240    elif rc == SCIP_LPERROR:
241        raise Exception('SCIP: error in LP solver!')
242    elif rc == SCIP_NOPROBLEM:
243        raise Exception('SCIP: no problem exists!')
244    elif rc == SCIP_INVALIDCALL:
245        raise Exception('SCIP: method cannot be called at this time'
246                            + ' in solution process!')
247    elif rc == SCIP_INVALIDDATA:
248        raise Exception('SCIP: error in input data!')
249    elif rc == SCIP_INVALIDRESULT:
250        raise Exception('SCIP: method returned an invalid result code!')
251    elif rc == SCIP_PLUGINNOTFOUND:
252        raise Exception('SCIP: a required plugin was not found !')
253    elif rc == SCIP_PARAMETERUNKNOWN:
254        raise KeyError('SCIP: the parameter with the given name was not found!')
255    elif rc == SCIP_PARAMETERWRONGTYPE:
256        raise LookupError('SCIP: the parameter is not of the expected type!')
257    elif rc == SCIP_PARAMETERWRONGVAL:
258        raise ValueError('SCIP: the value is invalid for the given parameter!')
259    elif rc == SCIP_KEYALREADYEXISTING:
260        raise KeyError('SCIP: the given key is already existing in table!')
261    elif rc == SCIP_MAXDEPTHLEVEL:
262        raise Exception('SCIP: maximal branching depth level exceeded!')
263    else:
264        raise Exception('SCIP: unknown return code!')
265
266cdef class Event:
267    """Base class holding a pointer to corresponding SCIP_EVENT"""
268
269    @staticmethod
270    cdef create(SCIP_EVENT* scip_event):
271        if scip_event == NULL:
272            raise Warning("cannot create Event with SCIP_EVENT* == NULL")
273        event = Event()
274        event.event = scip_event
275        return event
276
277    def getType(self):
278        """gets type of event"""
279        return SCIPeventGetType(self.event)
280
281    def __repr__(self):
282        return self.getType()
283
284    def getNewBound(self):
285        """gets new bound for a bound change event"""
286        return SCIPeventGetNewbound(self.event)
287
288    def getOldBound(self):
289        """gets old bound for a bound change event"""
290        return SCIPeventGetOldbound(self.event)
291
292    def getVar(self):
293        """gets variable for a variable event (var added, var deleted, var fixed, objective value or domain change, domain hole added or removed)"""
294        cdef SCIP_VAR* var = SCIPeventGetVar(self.event)
295        return Variable.create(var)
296
297    def getNode(self):
298        """gets node for a node or LP event"""
299        cdef SCIP_NODE* node = SCIPeventGetNode(self.event)
300        return Node.create(node)
301
302    def getRow(self):
303        """gets row for a row event"""
304        cdef SCIP_ROW* row = SCIPeventGetRow(self.event)
305        return Row.create(row)
306
307    def __hash__(self):
308        return hash(<size_t>self.event)
309
310    def __eq__(self, other):
311        return (self.__class__ == other.__class__
312                and self.event == (<Event>other).event)
313
314cdef class Column:
315    """Base class holding a pointer to corresponding SCIP_COL"""
316
317    @staticmethod
318    cdef create(SCIP_COL* scipcol):
319        if scipcol == NULL:
320            raise Warning("cannot create Column with SCIP_COL* == NULL")
321        col = Column()
322        col.scip_col = scipcol
323        return col
324
325    def getLPPos(self):
326        """gets position of column in current LP, or -1 if it is not in LP"""
327        return SCIPcolGetLPPos(self.scip_col)
328
329    def getBasisStatus(self):
330        """gets the basis status of a column in the LP solution, Note: returns basis status `zero` for columns not in the current SCIP LP"""
331        cdef SCIP_BASESTAT stat = SCIPcolGetBasisStatus(self.scip_col)
332        if stat == SCIP_BASESTAT_LOWER:
333            return "lower"
334        elif stat == SCIP_BASESTAT_BASIC:
335            return "basic"
336        elif stat == SCIP_BASESTAT_UPPER:
337            return "upper"
338        elif stat == SCIP_BASESTAT_ZERO:
339            return "zero"
340        else:
341            raise Exception('SCIP returned unknown base status!')
342
343    def isIntegral(self):
344        """returns whether the associated variable is of integral type (binary, integer, implicit integer)"""
345        return SCIPcolIsIntegral(self.scip_col)
346
347    def getVar(self):
348        """gets variable this column represents"""
349        cdef SCIP_VAR* var = SCIPcolGetVar(self.scip_col)
350        return Variable.create(var)
351
352    def getPrimsol(self):
353        """gets the primal LP solution of a column"""
354        return SCIPcolGetPrimsol(self.scip_col)
355
356    def getLb(self):
357        """gets lower bound of column"""
358        return SCIPcolGetLb(self.scip_col)
359
360    def getUb(self):
361        """gets upper bound of column"""
362        return SCIPcolGetUb(self.scip_col)
363
364    def __hash__(self):
365        return hash(<size_t>self.scip_col)
366
367    def __eq__(self, other):
368        return (self.__class__ == other.__class__
369                and self.scip_col == (<Column>other).scip_col)
370
371cdef class Row:
372    """Base class holding a pointer to corresponding SCIP_ROW"""
373
374    @staticmethod
375    cdef create(SCIP_ROW* sciprow):
376        if sciprow == NULL:
377            raise Warning("cannot create Row with SCIP_ROW* == NULL")
378        row = Row()
379        row.scip_row = sciprow
380        return row
381
382    property name:
383        def __get__(self):
384            cname = bytes( SCIProwGetName(self.scip_row) )
385            return cname.decode('utf-8')
386
387    def getLhs(self):
388        """returns the left hand side of row"""
389        return SCIProwGetLhs(self.scip_row)
390
391    def getRhs(self):
392        """returns the right hand side of row"""
393        return SCIProwGetRhs(self.scip_row)
394
395    def getConstant(self):
396        """gets constant shift of row"""
397        return SCIProwGetConstant(self.scip_row)
398
399    def getLPPos(self):
400        """gets position of row in current LP, or -1 if it is not in LP"""
401        return SCIProwGetLPPos(self.scip_row)
402
403    def getBasisStatus(self):
404        """gets the basis status of a row in the LP solution, Note: returns basis status `basic` for rows not in the current SCIP LP"""
405        cdef SCIP_BASESTAT stat = SCIProwGetBasisStatus(self.scip_row)
406        if stat == SCIP_BASESTAT_LOWER:
407            return "lower"
408        elif stat == SCIP_BASESTAT_BASIC:
409            return "basic"
410        elif stat == SCIP_BASESTAT_UPPER:
411            return "upper"
412        elif stat == SCIP_BASESTAT_ZERO:
413            # this shouldn't happen!
414            raise Exception('SCIP returned base status zero for a row!')
415        else:
416            raise Exception('SCIP returned unknown base status!')
417
418    def isIntegral(self):
419        """returns TRUE iff the activity of the row (without the row's constant) is always integral in a feasible solution """
420        return SCIProwIsIntegral(self.scip_row)
421
422    def isModifiable(self):
423        """returns TRUE iff row is modifiable during node processing (subject to column generation) """
424        return SCIProwIsModifiable(self.scip_row)
425
426    def isRemovable(self):
427        """returns TRUE iff row is removable from the LP (due to aging or cleanup)"""
428        return SCIProwIsRemovable(self.scip_row)
429
430    def getOrigintype(self):
431        """returns type of origin that created the row"""
432        return SCIProwGetOrigintype(self.scip_row)
433
434    def getNNonz(self):
435        """get number of nonzero entries in row vector"""
436        return SCIProwGetNNonz(self.scip_row)
437
438    def getNLPNonz(self):
439        """get number of nonzero entries in row vector that correspond to columns currently in the SCIP LP"""
440        return SCIProwGetNLPNonz(self.scip_row)
441
442    def getCols(self):
443        """gets list with columns of nonzero entries"""
444        cdef SCIP_COL** cols = SCIProwGetCols(self.scip_row)
445        return [Column.create(cols[i]) for i in range(self.getNNonz())]
446
447    def getVals(self):
448        """gets list with coefficients of nonzero entries"""
449        cdef SCIP_Real* vals = SCIProwGetVals(self.scip_row)
450        return [vals[i] for i in range(self.getNNonz())]
451
452    def __hash__(self):
453        return hash(<size_t>self.scip_row)
454
455    def __eq__(self, other):
456        return (self.__class__ == other.__class__
457                and self.scip_row == (<Row>other).scip_row)
458
459cdef class NLRow:
460    """Base class holding a pointer to corresponding SCIP_NLROW"""
461
462    @staticmethod
463    cdef create(SCIP_NLROW* scipnlrow):
464        if scipnlrow == NULL:
465            raise Warning("cannot create NLRow with SCIP_NLROW* == NULL")
466        nlrow = NLRow()
467        nlrow.scip_nlrow = scipnlrow
468        return nlrow
469
470    property name:
471        def __get__(self):
472            cname = bytes( SCIPnlrowGetName(self.scip_nlrow) )
473            return cname.decode('utf-8')
474
475    def getConstant(self):
476        """returns the constant of a nonlinear row"""
477        return SCIPnlrowGetConstant(self.scip_nlrow)
478
479    def getLinearTerms(self):
480        """returns a list of tuples (var, coef) representing the linear part of a nonlinear row"""
481        cdef SCIP_VAR** linvars = SCIPnlrowGetLinearVars(self.scip_nlrow)
482        cdef SCIP_Real* lincoefs = SCIPnlrowGetLinearCoefs(self.scip_nlrow)
483        cdef int nlinvars = SCIPnlrowGetNLinearVars(self.scip_nlrow)
484        return [(Variable.create(linvars[i]), lincoefs[i]) for i in range(nlinvars)]
485
486    def getQuadraticTerms(self):
487        """returns a list of tuples (var1, var2, coef) representing the quadratic part of a nonlinear row"""
488        cdef int nquadvars
489        cdef SCIP_VAR** quadvars
490        cdef int nquadelems
491        cdef SCIP_QUADELEM* quadelems
492
493        SCIPnlrowGetQuadData(self.scip_nlrow, &nquadvars, &quadvars, &nquadelems, &quadelems)
494
495        quadterms = []
496        for i in range(nquadelems):
497            x = Variable.create(quadvars[quadelems[i].idx1])
498            y = Variable.create(quadvars[quadelems[i].idx2])
499            coef = quadelems[i].coef
500            quadterms.append((x,y,coef))
501        return quadterms
502
503    def hasExprtree(self):
504        """returns whether there exists an expression tree in a nonlinear row"""
505        cdef SCIP_EXPRTREE* exprtree
506
507        exprtree = SCIPnlrowGetExprtree(self.scip_nlrow)
508        return exprtree != NULL
509
510    def getLhs(self):
511        """returns the left hand side of a nonlinear row"""
512        return SCIPnlrowGetLhs(self.scip_nlrow)
513
514    def getRhs(self):
515        """returns the right hand side of a nonlinear row"""
516        return SCIPnlrowGetRhs(self.scip_nlrow)
517
518    def getDualsol(self):
519        """gets the dual NLP solution of a nonlinear row"""
520        return SCIPnlrowGetDualsol(self.scip_nlrow)
521
522    def __hash__(self):
523        return hash(<size_t>self.scip_nlrow)
524
525    def __eq__(self, other):
526        return (self.__class__ == other.__class__
527                and self.scip_nlrow == (<NLRow>other).scip_nlrow)
528
529cdef class Solution:
530    """Base class holding a pointer to corresponding SCIP_SOL"""
531
532    @staticmethod
533    cdef create(SCIP* scip, SCIP_SOL* scip_sol):
534        if scip == NULL:
535            raise Warning("cannot create Solution with SCIP* == NULL")
536        sol = Solution()
537        sol.sol = scip_sol
538        sol.scip = scip
539        return sol
540
541    def __getitem__(self, Variable var):
542        return SCIPgetSolVal(self.scip, self.sol, var.scip_var)
543
544    def __setitem__(self, Variable var, value):
545        PY_SCIP_CALL(SCIPsetSolVal(self.scip, self.sol, var.scip_var, value))
546
547    def __repr__(self):
548        cdef SCIP_VAR* scip_var
549
550        vals = {}
551        for i in range(SCIPgetNVars(self.scip)):
552            scip_var = SCIPgetVars(self.scip)[i]
553
554            # extract name
555            cname = bytes(SCIPvarGetName(scip_var))
556            name = cname.decode('utf-8')
557
558            vals[name] = SCIPgetSolVal(self.scip, self.sol, scip_var)
559        return str(vals)
560
561cdef class BoundChange:
562    """Bound change."""
563
564    @staticmethod
565    cdef create(SCIP_BOUNDCHG* scip_boundchg):
566        if scip_boundchg == NULL:
567            raise Warning("cannot create BoundChange with SCIP_BOUNDCHG* == NULL")
568        boundchg = BoundChange()
569        boundchg.scip_boundchg = scip_boundchg
570        return boundchg
571
572    def getNewBound(self):
573        """Returns the new value of the bound in the bound change."""
574        return SCIPboundchgGetNewbound(self.scip_boundchg)
575
576    def getVar(self):
577        """Returns the variable of the bound change."""
578        return Variable.create(SCIPboundchgGetVar(self.scip_boundchg))
579
580    def getBoundchgtype(self):
581        """Returns the bound change type of the bound change."""
582        return SCIPboundchgGetBoundchgtype(self.scip_boundchg)
583
584    def getBoundtype(self):
585        """Returns the bound type of the bound change."""
586        return SCIPboundchgGetBoundtype(self.scip_boundchg)
587
588    def isRedundant(self):
589        """Returns whether the bound change is redundant due to a more global bound that is at least as strong."""
590        return SCIPboundchgIsRedundant(self.scip_boundchg)
591
592    def __repr__(self):
593        return "{} {} {}".format(self.getVar(),
594                                 _SCIP_BOUNDTYPE_TO_STRING[self.getBoundtype()],
595                                 self.getNewBound())
596
597cdef class DomainChanges:
598    """Set of domain changes."""
599
600    @staticmethod
601    cdef create(SCIP_DOMCHG* scip_domchg):
602        if scip_domchg == NULL:
603            raise Warning("cannot create DomainChanges with SCIP_DOMCHG* == NULL")
604        domchg = DomainChanges()
605        domchg.scip_domchg = scip_domchg
606        return domchg
607
608    def getBoundchgs(self):
609        """Returns the bound changes in the domain change."""
610        nboundchgs = SCIPdomchgGetNBoundchgs(self.scip_domchg)
611        return [BoundChange.create(SCIPdomchgGetBoundchg(self.scip_domchg, i))
612                for i in range(nboundchgs)]
613
614cdef class Node:
615    """Base class holding a pointer to corresponding SCIP_NODE"""
616
617    @staticmethod
618    cdef create(SCIP_NODE* scipnode):
619        if scipnode == NULL:
620            return None
621        node = Node()
622        node.scip_node = scipnode
623        return node
624
625    def getParent(self):
626        """Retrieve parent node (or None if the node has no parent node)."""
627        return Node.create(SCIPnodeGetParent(self.scip_node))
628
629    def getNumber(self):
630        """Retrieve number of node."""
631        return SCIPnodeGetNumber(self.scip_node)
632
633    def getDepth(self):
634        """Retrieve depth of node."""
635        return SCIPnodeGetDepth(self.scip_node)
636
637    def getType(self):
638        """Retrieve type of node."""
639        return SCIPnodeGetType(self.scip_node)
640
641    def getLowerbound(self):
642        """Retrieve lower bound of node."""
643        return SCIPnodeGetLowerbound(self.scip_node)
644
645    def getEstimate(self):
646        """Retrieve the estimated value of the best feasible solution in subtree of the node"""
647        return SCIPnodeGetEstimate(self.scip_node)
648
649    def getAddedConss(self):
650        """Retrieve all constraints added at this node."""
651        cdef int addedconsssize = SCIPnodeGetNAddedConss(self.scip_node)
652        if addedconsssize == 0:
653            return []
654        cdef SCIP_CONS** addedconss = <SCIP_CONS**> malloc(addedconsssize * sizeof(SCIP_CONS*))
655        cdef int nconss
656        SCIPnodeGetAddedConss(self.scip_node, addedconss, &nconss, addedconsssize)
657        assert nconss == addedconsssize
658        constraints = [Constraint.create(addedconss[i]) for i in range(nconss)]
659        free(addedconss)
660        return constraints
661
662    def getNAddedConss(self):
663        """Retrieve number of added constraints at this node"""
664        return SCIPnodeGetNAddedConss(self.scip_node)
665
666    def isActive(self):
667        """Is the node in the path to the current node?"""
668        return SCIPnodeIsActive(self.scip_node)
669
670    def isPropagatedAgain(self):
671        """Is the node marked to be propagated again?"""
672        return SCIPnodeIsPropagatedAgain(self.scip_node)
673
674    def getNParentBranchings(self):
675        """Retrieve the number of variable branchings that were performed in the parent node to create this node."""
676        cdef SCIP_VAR* dummy_branchvars
677        cdef SCIP_Real dummy_branchbounds
678        cdef SCIP_BOUNDTYPE dummy_boundtypes
679        cdef int nbranchvars
680        # This is a hack: the SCIP interface has no function to directly get the
681        # number of parent branchings, i.e., SCIPnodeGetNParentBranchings() does
682        # not exist.
683        SCIPnodeGetParentBranchings(self.scip_node, &dummy_branchvars,
684                                    &dummy_branchbounds, &dummy_boundtypes,
685                                    &nbranchvars, 0)
686        return nbranchvars
687
688    def getParentBranchings(self):
689        """Retrieve the set of variable branchings that were performed in the parent node to create this node."""
690        cdef int nbranchvars = self.getNParentBranchings()
691        if nbranchvars == 0:
692            return None
693
694        cdef SCIP_VAR** branchvars = <SCIP_VAR**> malloc(nbranchvars * sizeof(SCIP_VAR*))
695        cdef SCIP_Real* branchbounds = <SCIP_Real*> malloc(nbranchvars * sizeof(SCIP_Real))
696        cdef SCIP_BOUNDTYPE* boundtypes = <SCIP_BOUNDTYPE*> malloc(nbranchvars * sizeof(SCIP_BOUNDTYPE))
697
698        SCIPnodeGetParentBranchings(self.scip_node, branchvars, branchbounds,
699                                    boundtypes, &nbranchvars, nbranchvars)
700
701        py_variables = [Variable.create(branchvars[i]) for i in range(nbranchvars)]
702        py_branchbounds = [branchbounds[i] for i in range(nbranchvars)]
703        py_boundtypes = [boundtypes[i] for i in range(nbranchvars)]
704
705        free(boundtypes)
706        free(branchbounds)
707        free(branchvars)
708        return py_variables, py_branchbounds, py_boundtypes
709
710    def getNDomchg(self):
711        """Retrieve the number of bound changes due to branching, constraint propagation, and propagation."""
712        cdef int nbranchings
713        cdef int nconsprop
714        cdef int nprop
715        SCIPnodeGetNDomchg(self.scip_node, &nbranchings, &nconsprop, &nprop)
716        return nbranchings, nconsprop, nprop
717
718    def getDomchg(self):
719        """Retrieve domain changes for this node."""
720        cdef SCIP_DOMCHG* domchg = SCIPnodeGetDomchg(self.scip_node)
721        if domchg == NULL:
722            return None
723        return DomainChanges.create(domchg)
724
725    def __hash__(self):
726        return hash(<size_t>self.scip_node)
727
728    def __eq__(self, other):
729        return (self.__class__ == other.__class__
730                and self.scip_node == (<Node>other).scip_node)
731
732cdef class Variable(Expr):
733    """Is a linear expression and has SCIP_VAR*"""
734
735    @staticmethod
736    cdef create(SCIP_VAR* scipvar):
737        if scipvar == NULL:
738            raise Warning("cannot create Variable with SCIP_VAR* == NULL")
739        var = Variable()
740        var.scip_var = scipvar
741        Expr.__init__(var, {Term(var) : 1.0})
742        return var
743
744    property name:
745        def __get__(self):
746            cname = bytes( SCIPvarGetName(self.scip_var) )
747            return cname.decode('utf-8')
748
749    def ptr(self):
750        """ """
751        return <size_t>(self.scip_var)
752
753    def __repr__(self):
754        return self.name
755
756    def vtype(self):
757        """Retrieve the variables type (BINARY, INTEGER, IMPLINT or CONTINUOUS)"""
758        vartype = SCIPvarGetType(self.scip_var)
759        if vartype == SCIP_VARTYPE_BINARY:
760            return "BINARY"
761        elif vartype == SCIP_VARTYPE_INTEGER:
762            return "INTEGER"
763        elif vartype == SCIP_VARTYPE_CONTINUOUS:
764            return "CONTINUOUS"
765        elif vartype == SCIP_VARTYPE_IMPLINT:
766            return "IMPLINT"
767
768    def isOriginal(self):
769        """Retrieve whether the variable belongs to the original problem"""
770        return SCIPvarIsOriginal(self.scip_var)
771
772    def isInLP(self):
773        """Retrieve whether the variable is a COLUMN variable that is member of the current LP"""
774        return SCIPvarIsInLP(self.scip_var)
775
776
777    def getIndex(self):
778        """Retrieve the unique index of the variable."""
779        return SCIPvarGetIndex(self.scip_var)
780
781    def getCol(self):
782        """Retrieve column of COLUMN variable"""
783        cdef SCIP_COL* scip_col
784        scip_col = SCIPvarGetCol(self.scip_var)
785        return Column.create(scip_col)
786
787    def getLbOriginal(self):
788        """Retrieve original lower bound of variable"""
789        return SCIPvarGetLbOriginal(self.scip_var)
790
791    def getUbOriginal(self):
792        """Retrieve original upper bound of variable"""
793        return SCIPvarGetUbOriginal(self.scip_var)
794
795    def getLbGlobal(self):
796        """Retrieve global lower bound of variable"""
797        return SCIPvarGetLbGlobal(self.scip_var)
798
799    def getUbGlobal(self):
800        """Retrieve global upper bound of variable"""
801        return SCIPvarGetUbGlobal(self.scip_var)
802
803    def getLbLocal(self):
804        """Retrieve current lower bound of variable"""
805        return SCIPvarGetLbLocal(self.scip_var)
806
807    def getUbLocal(self):
808        """Retrieve current upper bound of variable"""
809        return SCIPvarGetUbLocal(self.scip_var)
810
811    def getObj(self):
812        """Retrieve current objective value of variable"""
813        return SCIPvarGetObj(self.scip_var)
814
815    def getLPSol(self):
816        """Retrieve the current LP solution value of variable"""
817        return SCIPvarGetLPSol(self.scip_var)
818
819cdef class Constraint:
820    """Base class holding a pointer to corresponding SCIP_CONS"""
821
822    @staticmethod
823    cdef create(SCIP_CONS* scipcons):
824        if scipcons == NULL:
825            raise Warning("cannot create Constraint with SCIP_CONS* == NULL")
826        cons = Constraint()
827        cons.scip_cons = scipcons
828        return cons
829
830    property name:
831        def __get__(self):
832            cname = bytes( SCIPconsGetName(self.scip_cons) )
833            return cname.decode('utf-8')
834
835    def __repr__(self):
836        return self.name
837
838    def isOriginal(self):
839        """Retrieve whether the constraint belongs to the original problem"""
840        return SCIPconsIsOriginal(self.scip_cons)
841
842    def isInitial(self):
843        """Retrieve True if the relaxation of the constraint should be in the initial LP"""
844        return SCIPconsIsInitial(self.scip_cons)
845
846    def isSeparated(self):
847        """Retrieve True if constraint should be separated during LP processing"""
848        return SCIPconsIsSeparated(self.scip_cons)
849
850    def isEnforced(self):
851        """Retrieve True if constraint should be enforced during node processing"""
852        return SCIPconsIsEnforced(self.scip_cons)
853
854    def isChecked(self):
855        """Retrieve True if constraint should be checked for feasibility"""
856        return SCIPconsIsChecked(self.scip_cons)
857
858    def isPropagated(self):
859        """Retrieve True if constraint should be propagated during node processing"""
860        return SCIPconsIsPropagated(self.scip_cons)
861
862    def isLocal(self):
863        """Retrieve True if constraint is only locally valid or not added to any (sub)problem"""
864        return SCIPconsIsLocal(self.scip_cons)
865
866    def isModifiable(self):
867        """Retrieve True if constraint is modifiable (subject to column generation)"""
868        return SCIPconsIsModifiable(self.scip_cons)
869
870    def isDynamic(self):
871        """Retrieve True if constraint is subject to aging"""
872        return SCIPconsIsDynamic(self.scip_cons)
873
874    def isRemovable(self):
875        """Retrieve True if constraint's relaxation should be removed from the LP due to aging or cleanup"""
876        return SCIPconsIsRemovable(self.scip_cons)
877
878    def isStickingAtNode(self):
879        """Retrieve True if constraint is only locally valid or not added to any (sub)problem"""
880        return SCIPconsIsStickingAtNode(self.scip_cons)
881
882    def isActive(self):
883        """returns True iff constraint is active in the current node"""
884        return SCIPconsIsActive(self.scip_cons)
885
886    def isLinear(self):
887        """Retrieve True if constraint is linear"""
888        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
889        return constype == 'linear'
890
891    def isQuadratic(self):
892        """Retrieve True if constraint is quadratic"""
893        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
894        return constype == 'quadratic'
895
896    def __hash__(self):
897        return hash(<size_t>self.scip_cons)
898
899    def __eq__(self, other):
900        return (self.__class__ == other.__class__
901                and self.scip_cons == (<Constraint>other).scip_cons)
902
903
904cdef void relayMessage(SCIP_MESSAGEHDLR *messagehdlr, FILE *file, const char *msg):
905    sys.stdout.write(msg.decode('UTF-8'))
906
907cdef void relayErrorMessage(void *messagehdlr, FILE *file, const char *msg):
908    sys.stderr.write(msg.decode('UTF-8'))
909
910# - remove create(), includeDefaultPlugins(), createProbBasic() methods
911# - replace free() by "destructor"
912# - interface SCIPfreeProb()
913##
914#@anchor Model
915##
916cdef class Model:
917    """Main class holding a pointer to SCIP for managing most interactions"""
918
919    def __init__(self, problemName='model', defaultPlugins=True, Model sourceModel=None, origcopy=False, globalcopy=True, enablepricing=False, createscip=True, threadsafe=False):
920        """
921        :param problemName: name of the problem (default 'model')
922        :param defaultPlugins: use default plugins? (default True)
923        :param sourceModel: create a copy of the given Model instance (default None)
924        :param origcopy: whether to call copy or copyOrig (default False)
925        :param globalcopy: whether to create a global or a local copy (default True)
926        :param enablepricing: whether to enable pricing in copy (default False)
927        :param createscip: initialize the Model object and creates a SCIP instance
928        :param threadsafe: False if data can be safely shared between the source and target problem
929        """
930        if self.version() < MAJOR:
931            raise Exception("linked SCIP is not compatible to this version of PySCIPOpt - use at least version", MAJOR)
932        if self.version() < MAJOR + MINOR/10.0 + PATCH/100.0:
933            warnings.warn("linked SCIP {} is not recommended for this version of PySCIPOpt - use version {}.{}.{}".format(self.version(), MAJOR, MINOR, PATCH))
934
935        self._freescip = True
936        self._modelvars = {}
937
938        if not createscip:
939            # if no SCIP instance should be created, then an empty Model object is created.
940            self._scip = NULL
941            self._bestSol = None
942            self._freescip = False
943        elif sourceModel is None:
944            PY_SCIP_CALL(SCIPcreate(&self._scip))
945            self._bestSol = None
946            if defaultPlugins:
947                self.includeDefaultPlugins()
948            self.createProbBasic(problemName)
949        else:
950            PY_SCIP_CALL(SCIPcreate(&self._scip))
951            self._bestSol = <Solution> sourceModel._bestSol
952            n = str_conversion(problemName)
953            if origcopy:
954                PY_SCIP_CALL(SCIPcopyOrig(sourceModel._scip, self._scip, NULL, NULL, n, enablepricing, threadsafe, True, self._valid))
955            else:
956                PY_SCIP_CALL(SCIPcopy(sourceModel._scip, self._scip, NULL, NULL, n, globalcopy, enablepricing, threadsafe, True, self._valid))
957
958    def __dealloc__(self):
959        # call C function directly, because we can no longer call this object's methods, according to
960        # http://docs.cython.org/src/reference/extension_types.html#finalization-dealloc
961        if self._scip is not NULL and self._freescip and PY_SCIP_CALL:
962           PY_SCIP_CALL( SCIPfree(&self._scip) )
963
964    def __hash__(self):
965        return hash(<size_t>self._scip)
966
967    def __eq__(self, other):
968        return (self.__class__ == other.__class__
969                and self._scip == (<Model>other)._scip)
970
971    @staticmethod
972    cdef create(SCIP* scip):
973        """Creates a model and appropriately assigns the scip and bestsol parameters
974        """
975        if scip == NULL:
976            raise Warning("cannot create Model with SCIP* == NULL")
977        model = Model(createscip=False)
978        model._scip = scip
979        model._bestSol = Solution.create(scip, SCIPgetBestSol(scip))
980        return model
981
982    @property
983    def _freescip(self):
984        """Return whether the underlying Scip pointer gets deallocted when the current
985        object is deleted.
986        """
987        return self._freescip
988
989    @_freescip.setter
990    def _freescip(self, val):
991        """Set whether the underlying Scip pointer gets deallocted when the current
992        object is deleted.
993        """
994        self._freescip = val
995
996    @cython.always_allow_keywords(True)
997    @staticmethod
998    def from_ptr(capsule, take_ownership):
999        """Create a Model from a given pointer.
1000
1001        :param cpasule: The PyCapsule containing the SCIP pointer under the name "scip".
1002        :param take_ownership: Whether the newly created Model assumes ownership of the
1003        underlying Scip pointer (see `_freescip`).
1004        """
1005        if not PyCapsule_IsValid(capsule, "scip"):
1006            raise ValueError("The given capsule does not contain a valid scip pointer")
1007        model = Model.create(<SCIP*>PyCapsule_GetPointer(capsule, "scip"))
1008        model._freescip = take_ownership
1009        return model
1010
1011    @cython.always_allow_keywords(True)
1012    def to_ptr(self, give_ownership):
1013        """Return the underlying Scip pointer to the current Model.
1014
1015        :param give_ownership: Whether the current Model gives away ownership of the
1016        underlying Scip pointer (see `_freescip`).
1017        :return capsule: The underlying pointer to the current Model, wrapped in a
1018        PyCapsule under the name "scip".
1019        """
1020        capsule = PyCapsule_New(<void*>self._scip, "scip", NULL)
1021        if give_ownership:
1022            self._freescip = False
1023        return capsule
1024
1025    def includeDefaultPlugins(self):
1026        """Includes all default plug-ins into SCIP"""
1027        PY_SCIP_CALL(SCIPincludeDefaultPlugins(self._scip))
1028
1029    def createProbBasic(self, problemName='model'):
1030        """Create new problem instance with given name
1031
1032        :param problemName: name of model or problem (Default value = 'model')
1033
1034        """
1035        n = str_conversion(problemName)
1036        PY_SCIP_CALL(SCIPcreateProbBasic(self._scip, n))
1037
1038    def freeProb(self):
1039        """Frees problem and solution process data"""
1040        PY_SCIP_CALL(SCIPfreeProb(self._scip))
1041
1042    def freeTransform(self):
1043        """Frees all solution process data including presolving and transformed problem, only original problem is kept"""
1044        PY_SCIP_CALL(SCIPfreeTransform(self._scip))
1045
1046    def version(self):
1047        """Retrieve SCIP version"""
1048        return SCIPversion()
1049
1050    def printVersion(self):
1051        """Print version, copyright information and compile mode"""
1052        SCIPprintVersion(self._scip, NULL)
1053
1054    def getProbName(self):
1055        """Retrieve problem name"""
1056        return bytes(SCIPgetProbName(self._scip)).decode('UTF-8')
1057
1058    def getTotalTime(self):
1059        """Retrieve the current total SCIP time in seconds, i.e. the total time since the SCIP instance has been created"""
1060        return SCIPgetTotalTime(self._scip)
1061
1062    def getSolvingTime(self):
1063        """Retrieve the current solving time in seconds"""
1064        return SCIPgetSolvingTime(self._scip)
1065
1066    def getReadingTime(self):
1067        """Retrieve the current reading time in seconds"""
1068        return SCIPgetReadingTime(self._scip)
1069
1070    def getPresolvingTime(self):
1071        """Retrieve the curernt presolving time in seconds"""
1072        return SCIPgetPresolvingTime(self._scip)
1073
1074    def getNLPIterations(self):
1075        """Retrieve the total number of LP iterations so far."""
1076        return SCIPgetNLPIterations(self._scip)
1077
1078    def getNNodes(self):
1079        """Retrieve the total number of processed nodes."""
1080        return SCIPgetNNodes(self._scip)
1081
1082    def getNFeasibleLeaves(self):
1083        """Retrieve number of leaf nodes processed with feasible relaxation solution."""
1084        return SCIPgetNFeasibleLeaves(self._scip)
1085
1086    def getNInfeasibleLeaves(self):
1087        """gets number of infeasible leaf nodes processed."""
1088        return SCIPgetNInfeasibleLeaves(self._scip)
1089
1090    def getNLeaves(self):
1091        """gets number of leaves in the tree."""
1092        return SCIPgetNLeaves(self._scip)
1093
1094    def getNChildren(self):
1095        """gets number of children of focus node."""
1096        return SCIPgetNChildren(self._scip)
1097
1098    def getNSiblings(self):
1099        """gets number of siblings of focus node."""
1100        return SCIPgetNSiblings(self._scip)
1101
1102    def getCurrentNode(self):
1103        """Retrieve current node."""
1104        return Node.create(SCIPgetCurrentNode(self._scip))
1105
1106    def getGap(self):
1107        """Retrieve the gap, i.e. |(primalbound - dualbound)/min(|primalbound|,|dualbound|)|."""
1108        return SCIPgetGap(self._scip)
1109
1110    def getDepth(self):
1111        """Retrieve the depth of the current node"""
1112        return SCIPgetDepth(self._scip)
1113
1114    def infinity(self):
1115        """Retrieve SCIP's infinity value"""
1116        return SCIPinfinity(self._scip)
1117
1118    def epsilon(self):
1119        """Retrieve epsilon for e.g. equality checks"""
1120        return SCIPepsilon(self._scip)
1121
1122    def feastol(self):
1123        """Retrieve feasibility tolerance"""
1124        return SCIPfeastol(self._scip)
1125
1126    def feasFrac(self, value):
1127        """returns fractional part of value, i.e. x - floor(x) in feasible tolerance: x - floor(x+feastol)"""
1128        return SCIPfeasFrac(self._scip, value)
1129
1130    def frac(self, value):
1131        """returns fractional part of value, i.e. x - floor(x) in epsilon tolerance: x - floor(x+eps)"""
1132        return SCIPfrac(self._scip, value)
1133
1134    def isZero(self, value):
1135        """returns whether abs(value) < eps"""
1136        return SCIPisZero(self._scip, value)
1137
1138    def isFeasZero(self, value):
1139        """returns whether abs(value) < feastol"""
1140        return SCIPisFeasZero(self._scip, value)
1141
1142    def isInfinity(self, value):
1143        """returns whether value is SCIP's infinity"""
1144        return SCIPisInfinity(self._scip, value)
1145
1146    def isFeasNegative(self, value):
1147        """returns whether value < -feastol"""
1148        return SCIPisFeasNegative(self._scip, value)
1149
1150    def isFeasIntegral(self, value):
1151        """returns whether value is integral within the LP feasibility bounds"""
1152        return SCIPisFeasIntegral(self._scip, value)
1153
1154    def isEQ(self, val1, val2):
1155        """checks, if values are in range of epsilon"""
1156        return SCIPisEQ(self._scip, val1, val2)
1157
1158    def isFeasEQ(self, val1, val2):
1159        """checks, if relative difference of values is in range of feasibility tolerance"""
1160        return SCIPisFeasEQ(self._scip, val1, val2)
1161
1162    def isLE(self, val1, val2):
1163        """returns whether val1 <= val2 + eps"""
1164        return SCIPisLE(self._scip, val1, val2)
1165
1166    def isLT(self, val1, val2):
1167        """returns whether val1 < val2 - eps"""
1168        return SCIPisLT(self._scip, val1, val2)
1169
1170    def isGE(self, val1, val2):
1171        """returns whether val1 >= val2 - eps"""
1172        return SCIPisGE(self._scip, val1, val2)
1173
1174    def isGT(self, val1, val2):
1175        """returns whether val1 > val2 + eps"""
1176        return SCIPisGT(self._scip, val1, val2)
1177
1178    def getCondition(self, exact=False):
1179        """Get the current LP's condition number
1180
1181        :param exact: whether to get an estimate or the exact value (Default value = False)
1182
1183        """
1184        cdef SCIP_LPI* lpi
1185        PY_SCIP_CALL(SCIPgetLPI(self._scip, &lpi))
1186        cdef SCIP_Real quality = 0
1187        if exact:
1188            PY_SCIP_CALL(SCIPlpiGetRealSolQuality(lpi, SCIP_LPSOLQUALITY_EXACTCONDITION, &quality))
1189        else:
1190            PY_SCIP_CALL(SCIPlpiGetRealSolQuality(lpi, SCIP_LPSOLQUALITY_ESTIMCONDITION, &quality))
1191
1192        return quality
1193
1194    def enableReoptimization(self, enable=True):
1195        """include specific heuristics and branching rules for reoptimization"""
1196        PY_SCIP_CALL(SCIPenableReoptimization(self._scip, enable))
1197
1198    def lpiGetIterations(self):
1199        """Get the iteration count of the last solved LP"""
1200        cdef SCIP_LPI* lpi
1201        PY_SCIP_CALL(SCIPgetLPI(self._scip, &lpi))
1202        cdef int iters = 0
1203        PY_SCIP_CALL(SCIPlpiGetIterations(lpi, &iters))
1204        return iters
1205
1206    # Objective function
1207
1208    def setMinimize(self):
1209        """Set the objective sense to minimization."""
1210        PY_SCIP_CALL(SCIPsetObjsense(self._scip, SCIP_OBJSENSE_MINIMIZE))
1211
1212    def setMaximize(self):
1213        """Set the objective sense to maximization."""
1214        PY_SCIP_CALL(SCIPsetObjsense(self._scip, SCIP_OBJSENSE_MAXIMIZE))
1215
1216    def setObjlimit(self, objlimit):
1217        """Set a limit on the objective function.
1218        Only solutions with objective value better than this limit are accepted.
1219
1220        :param objlimit: limit on the objective function
1221
1222        """
1223        PY_SCIP_CALL(SCIPsetObjlimit(self._scip, objlimit))
1224
1225    def getObjlimit(self):
1226        """returns current limit on objective function."""
1227        return SCIPgetObjlimit(self._scip)
1228
1229    def setObjective(self, coeffs, sense = 'minimize', clear = 'true'):
1230        """Establish the objective function as a linear expression.
1231
1232        :param coeffs: the coefficients
1233        :param sense: the objective sense (Default value = 'minimize')
1234        :param clear: set all other variables objective coefficient to zero (Default value = 'true')
1235
1236        """
1237        cdef SCIP_VAR** _vars
1238        cdef int _nvars
1239
1240        # turn the constant value into an Expr instance for further processing
1241        if not isinstance(coeffs, Expr):
1242            assert(_is_number(coeffs)), "given coefficients are neither Expr or number but %s" % coeffs.__class__.__name__
1243            coeffs = Expr() + coeffs
1244
1245        if coeffs.degree() > 1:
1246            raise ValueError("Nonlinear objective functions are not supported!")
1247        if coeffs[CONST] != 0.0:
1248            self.addObjoffset(coeffs[CONST])
1249
1250        if clear:
1251            # clear existing objective function
1252            _vars = SCIPgetOrigVars(self._scip)
1253            _nvars = SCIPgetNOrigVars(self._scip)
1254            for i in range(_nvars):
1255                PY_SCIP_CALL(SCIPchgVarObj(self._scip, _vars[i], 0.0))
1256
1257        for term, coef in coeffs.terms.items():
1258            # avoid CONST term of Expr
1259            if term != CONST:
1260                assert len(term) == 1
1261                var = <Variable>term[0]
1262                PY_SCIP_CALL(SCIPchgVarObj(self._scip, var.scip_var, coef))
1263
1264        if sense == "minimize":
1265            self.setMinimize()
1266        elif sense == "maximize":
1267            self.setMaximize()
1268        else:
1269            raise Warning("unrecognized optimization sense: %s" % sense)
1270
1271    def getObjective(self):
1272        """Retrieve objective function as Expr"""
1273        variables = self.getVars()
1274        objective = Expr()
1275        for var in variables:
1276            coeff = var.getObj()
1277            if coeff != 0:
1278                objective += coeff * var
1279        objective.normalize()
1280        return objective
1281
1282    def addObjoffset(self, offset, solutions = False):
1283        """Add constant offset to objective
1284
1285        :param offset: offset to add
1286        :param solutions: add offset also to existing solutions (Default value = False)
1287
1288        """
1289        if solutions:
1290            PY_SCIP_CALL(SCIPaddObjoffset(self._scip, offset))
1291        else:
1292            PY_SCIP_CALL(SCIPaddOrigObjoffset(self._scip, offset))
1293
1294    def getObjoffset(self, original = True):
1295        """Retrieve constant objective offset
1296
1297        :param original: offset of original or transformed problem (Default value = True)
1298
1299        """
1300        if original:
1301            return SCIPgetOrigObjoffset(self._scip)
1302        else:
1303            return SCIPgetTransObjoffset(self._scip)
1304
1305
1306    def setObjIntegral(self):
1307        """informs SCIP that the objective value is always integral in every feasible solution
1308        Note: This function should be used to inform SCIP that the objective function is integral, helping to improve the
1309        performance. This is useful when using column generation. If no column generation (pricing) is used, SCIP
1310        automatically detects whether the objective function is integral or can be scaled to be integral. However, in
1311        any case, the user has to make sure that no variable is added during the solving process that destroys this
1312        property.
1313        """
1314        PY_SCIP_CALL(SCIPsetObjIntegral(self._scip))
1315
1316    def getLocalEstimate(self, original = False):
1317        """gets estimate of best primal solution w.r.t. original or transformed problem contained in current subtree
1318
1319        :param original: estimate of original or transformed problem (Default value = False)
1320        """
1321        if original:
1322            return SCIPgetLocalOrigEstimate(self._scip)
1323        else:
1324            return SCIPgetLocalTransEstimate(self._scip)
1325
1326    # Setting parameters
1327    def setPresolve(self, setting):
1328        """Set presolving parameter settings.
1329
1330        :param setting: the parameter settings (SCIP_PARAMSETTING)
1331
1332        """
1333        PY_SCIP_CALL(SCIPsetPresolving(self._scip, setting, True))
1334
1335    def setProbName(self, name):
1336        """Set problem name"""
1337        n = str_conversion(name)
1338        PY_SCIP_CALL(SCIPsetProbName(self._scip, n))
1339
1340    def setSeparating(self, setting):
1341        """Set separating parameter settings.
1342
1343        :param setting: the parameter settings (SCIP_PARAMSETTING)
1344
1345        """
1346        PY_SCIP_CALL(SCIPsetSeparating(self._scip, setting, True))
1347
1348    def setHeuristics(self, setting):
1349        """Set heuristics parameter settings.
1350
1351        :param setting: the parameter setting (SCIP_PARAMSETTING)
1352
1353        """
1354        PY_SCIP_CALL(SCIPsetHeuristics(self._scip, setting, True))
1355
1356    def disablePropagation(self, onlyroot=False):
1357        """Disables propagation in SCIP to avoid modifying the original problem during transformation.
1358
1359        :param onlyroot: use propagation when root processing is finished (Default value = False)
1360
1361        """
1362        self.setIntParam("propagating/maxroundsroot", 0)
1363        if not onlyroot:
1364            self.setIntParam("propagating/maxrounds", 0)
1365
1366    def writeProblem(self, filename='model.cip', trans=False):
1367        """Write current model/problem to a file.
1368
1369        :param filename: the name of the file to be used (Default value = 'model.cip')
1370        :param trans: indicates whether the transformed problem is written to file (Default value = False)
1371
1372        """
1373        fn = str_conversion(filename)
1374        fn, ext = splitext(fn)
1375        if len(ext) == 0:
1376            ext = str_conversion('.cip')
1377        fn = fn + ext
1378        ext = ext[1:]
1379        if trans:
1380            PY_SCIP_CALL(SCIPwriteTransProblem(self._scip, fn, ext, False))
1381        else:
1382            PY_SCIP_CALL(SCIPwriteOrigProblem(self._scip, fn, ext, False))
1383        print('wrote problem to file ' + str(fn))
1384
1385    # Variable Functions
1386
1387    def addVar(self, name='', vtype='C', lb=0.0, ub=None, obj=0.0, pricedVar = False):
1388        """Create a new variable. Default variable is non-negative and continuous.
1389
1390        :param name: name of the variable, generic if empty (Default value = '')
1391        :param vtype: type of the variable: 'C' continuous, 'I' integer, 'B' binary, and 'M' implicit integer
1392        (see https://www.scipopt.org/doc/html/FAQ.php#implicitinteger) (Default value = 'C')
1393        :param lb: lower bound of the variable, use None for -infinity (Default value = 0.0)
1394        :param ub: upper bound of the variable, use None for +infinity (Default value = None)
1395        :param obj: objective value of variable (Default value = 0.0)
1396        :param pricedVar: is the variable a pricing candidate? (Default value = False)
1397
1398        """
1399        cdef SCIP_VAR* scip_var
1400
1401        # replace empty name with generic one
1402        if name == '':
1403            name = 'x'+str(SCIPgetNVars(self._scip)+1)
1404        cname = str_conversion(name)
1405
1406        # replace None with corresponding infinity
1407        if lb is None:
1408            lb = -SCIPinfinity(self._scip)
1409        if ub is None:
1410            ub = SCIPinfinity(self._scip)
1411
1412        vtype = vtype.upper()
1413        if vtype in ['C', 'CONTINUOUS']:
1414            PY_SCIP_CALL(SCIPcreateVarBasic(self._scip, &scip_var, cname, lb, ub, obj, SCIP_VARTYPE_CONTINUOUS))
1415        elif vtype in ['B', 'BINARY']:
1416            if ub > 1.0:
1417                ub = 1.0
1418            if lb < 0.0:
1419                lb = 0.0
1420            PY_SCIP_CALL(SCIPcreateVarBasic(self._scip, &scip_var, cname, lb, ub, obj, SCIP_VARTYPE_BINARY))
1421        elif vtype in ['I', 'INTEGER']:
1422            PY_SCIP_CALL(SCIPcreateVarBasic(self._scip, &scip_var, cname, lb, ub, obj, SCIP_VARTYPE_INTEGER))
1423        elif vtype in ['M', 'IMPLINT']:
1424            PY_SCIP_CALL(SCIPcreateVarBasic(self._scip, &scip_var, cname, lb, ub, obj, SCIP_VARTYPE_IMPLINT))
1425        else:
1426            raise Warning("unrecognized variable type")
1427
1428        if pricedVar:
1429            PY_SCIP_CALL(SCIPaddPricedVar(self._scip, scip_var, 1.0))
1430        else:
1431            PY_SCIP_CALL(SCIPaddVar(self._scip, scip_var))
1432
1433        pyVar = Variable.create(scip_var)
1434
1435        # store variable in the model to avoid creating new python variable objects in getVars()
1436        assert not pyVar.ptr() in self._modelvars
1437        self._modelvars[pyVar.ptr()] = pyVar
1438
1439        #setting the variable data
1440        SCIPvarSetData(scip_var, <SCIP_VARDATA*>pyVar)
1441        PY_SCIP_CALL(SCIPreleaseVar(self._scip, &scip_var))
1442        return pyVar
1443
1444    def getTransformedVar(self, Variable var):
1445        """Retrieve the transformed variable.
1446
1447        :param Variable var: original variable to get the transformed of
1448
1449        """
1450        cdef SCIP_VAR* _tvar
1451        PY_SCIP_CALL(SCIPtransformVar(self._scip, var.scip_var, &_tvar))
1452        return Variable.create(_tvar)
1453
1454    def addVarLocks(self, Variable var, nlocksdown, nlocksup):
1455        """adds given values to lock numbers of variable for rounding
1456
1457        :param Variable var: variable to adjust the locks for
1458        :param nlocksdown: new number of down locks
1459        :param nlocksup: new number of up locks
1460
1461        """
1462        PY_SCIP_CALL(SCIPaddVarLocks(self._scip, var.scip_var, nlocksdown, nlocksup))
1463
1464    def fixVar(self, Variable var, val):
1465        """Fixes the variable var to the value val if possible.
1466
1467        :param Variable var: variable to fix
1468        :param val: float, the fix value
1469        :return: tuple (infeasible, fixed) of booleans
1470
1471        """
1472        cdef SCIP_Bool infeasible
1473        cdef SCIP_Bool fixed
1474        PY_SCIP_CALL(SCIPfixVar(self._scip, var.scip_var, val, &infeasible, &fixed))
1475        return infeasible, fixed
1476
1477    def delVar(self, Variable var):
1478        """Delete a variable.
1479
1480        :param var: the variable which shall be deleted
1481        :return: bool, was deleting succesful
1482
1483        """
1484        cdef SCIP_Bool deleted
1485        PY_SCIP_CALL(SCIPdelVar(self._scip, var.scip_var, &deleted))
1486        return deleted
1487
1488    def tightenVarLb(self, Variable var, lb, force=False):
1489        """Tighten the lower bound in preprocessing or current node, if the bound is tighter.
1490
1491        :param var: SCIP variable
1492        :param lb: possible new lower bound
1493        :param force: force tightening even if below bound strengthening tolerance
1494        :return: tuple of bools, (infeasible, tightened)
1495                    infeasible: whether new domain is empty
1496                    tightened: whether the bound was tightened
1497
1498        """
1499        cdef SCIP_Bool infeasible
1500        cdef SCIP_Bool tightened
1501        PY_SCIP_CALL(SCIPtightenVarLb(self._scip, var.scip_var, lb, force, &infeasible, &tightened))
1502        return infeasible, tightened
1503
1504
1505    def tightenVarUb(self, Variable var, ub, force=False):
1506        """Tighten the upper bound in preprocessing or current node, if the bound is tighter.
1507
1508        :param var: SCIP variable
1509        :param ub: possible new upper bound
1510        :param force: force tightening even if below bound strengthening tolerance
1511        :return: tuple of bools, (infeasible, tightened)
1512                    infeasible: whether new domain is empty
1513                    tightened: whether the bound was tightened
1514
1515        """
1516        cdef SCIP_Bool infeasible
1517        cdef SCIP_Bool tightened
1518        PY_SCIP_CALL(SCIPtightenVarUb(self._scip, var.scip_var, ub, force, &infeasible, &tightened))
1519        return infeasible, tightened
1520
1521
1522    def tightenVarUbGlobal(self, Variable var, ub, force=False):
1523        """Tighten the global upper bound, if the bound is tighter.
1524
1525        :param var: SCIP variable
1526        :param ub: possible new upper bound
1527        :param force: force tightening even if below bound strengthening tolerance
1528        :return: tuple of bools, (infeasible, tightened)
1529                    infeasible: whether new domain is empty
1530                    tightened: whether the bound was tightened
1531
1532        """
1533        cdef SCIP_Bool infeasible
1534        cdef SCIP_Bool tightened
1535        PY_SCIP_CALL(SCIPtightenVarUbGlobal(self._scip, var.scip_var, ub, force, &infeasible, &tightened))
1536        return infeasible, tightened
1537
1538    def tightenVarLbGlobal(self, Variable var, lb, force=False):
1539        """Tighten the global upper bound, if the bound is tighter.
1540
1541        :param var: SCIP variable
1542        :param lb: possible new upper bound
1543        :param force: force tightening even if below bound strengthening tolerance
1544        :return: tuple of bools, (infeasible, tightened)
1545                    infeasible: whether new domain is empty
1546                    tightened: whether the bound was tightened
1547
1548        """
1549        cdef SCIP_Bool infeasible
1550        cdef SCIP_Bool tightened
1551        PY_SCIP_CALL(SCIPtightenVarLbGlobal(self._scip, var.scip_var, lb, force, &infeasible, &tightened))
1552        return infeasible, tightened
1553
1554    def chgVarLb(self, Variable var, lb):
1555        """Changes the lower bound of the specified variable.
1556
1557        :param Variable var: variable to change bound of
1558        :param lb: new lower bound (set to None for -infinity)
1559
1560        """
1561        if lb is None:
1562           lb = -SCIPinfinity(self._scip)
1563        PY_SCIP_CALL(SCIPchgVarLb(self._scip, var.scip_var, lb))
1564
1565    def chgVarUb(self, Variable var, ub):
1566        """Changes the upper bound of the specified variable.
1567
1568        :param Variable var: variable to change bound of
1569        :param ub: new upper bound (set to None for +infinity)
1570
1571        """
1572        if ub is None:
1573           ub = SCIPinfinity(self._scip)
1574        PY_SCIP_CALL(SCIPchgVarUb(self._scip, var.scip_var, ub))
1575
1576
1577    def chgVarLbGlobal(self, Variable var, lb):
1578        """Changes the global lower bound of the specified variable.
1579
1580        :param Variable var: variable to change bound of
1581        :param lb: new lower bound (set to None for -infinity)
1582
1583        """
1584        if lb is None:
1585           lb = -SCIPinfinity(self._scip)
1586        PY_SCIP_CALL(SCIPchgVarLbGlobal(self._scip, var.scip_var, lb))
1587
1588    def chgVarUbGlobal(self, Variable var, ub):
1589        """Changes the global upper bound of the specified variable.
1590
1591        :param Variable var: variable to change bound of
1592        :param ub: new upper bound (set to None for +infinity)
1593
1594        """
1595        if ub is None:
1596           ub = SCIPinfinity(self._scip)
1597        PY_SCIP_CALL(SCIPchgVarUbGlobal(self._scip, var.scip_var, ub))
1598
1599    def chgVarLbNode(self, Node node, Variable var, lb):
1600        """Changes the lower bound of the specified variable at the given node.
1601
1602        :param Variable var: variable to change bound of
1603        :param lb: new lower bound (set to None for -infinity)
1604        """
1605
1606        if lb is None:
1607           lb = -SCIPinfinity(self._scip)
1608        PY_SCIP_CALL(SCIPchgVarLbNode(self._scip, node.scip_node, var.scip_var, lb))
1609
1610    def chgVarUbNode(self, Node node, Variable var, ub):
1611        """Changes the upper bound of the specified variable at the given node.
1612
1613        :param Variable var: variable to change bound of
1614        :param ub: new upper bound (set to None for +infinity)
1615
1616        """
1617        if ub is None:
1618           ub = SCIPinfinity(self._scip)
1619        PY_SCIP_CALL(SCIPchgVarUbNode(self._scip, node.scip_node, var.scip_var, ub))
1620
1621    def chgVarType(self, Variable var, vtype):
1622        """Changes the type of a variable
1623
1624        :param Variable var: variable to change type of
1625        :param vtype: new variable type
1626
1627        """
1628        cdef SCIP_Bool infeasible
1629        if vtype in ['C', 'CONTINUOUS']:
1630            PY_SCIP_CALL(SCIPchgVarType(self._scip, var.scip_var, SCIP_VARTYPE_CONTINUOUS, &infeasible))
1631        elif vtype in ['B', 'BINARY']:
1632            PY_SCIP_CALL(SCIPchgVarType(self._scip, var.scip_var, SCIP_VARTYPE_BINARY, &infeasible))
1633        elif vtype in ['I', 'INTEGER']:
1634            PY_SCIP_CALL(SCIPchgVarType(self._scip, var.scip_var, SCIP_VARTYPE_INTEGER, &infeasible))
1635        elif vtype in ['M', 'IMPLINT']:
1636            PY_SCIP_CALL(SCIPchgVarType(self._scip, var.scip_var, SCIP_VARTYPE_IMPLINT, &infeasible))
1637        else:
1638            raise Warning("unrecognized variable type")
1639        if infeasible:
1640            print('could not change variable type of variable %s' % var)
1641
1642    def getVars(self, transformed=False):
1643        """Retrieve all variables.
1644
1645        :param transformed: get transformed variables instead of original (Default value = False)
1646
1647        """
1648        cdef SCIP_VAR** _vars
1649        cdef SCIP_VAR* _var
1650        cdef int _nvars
1651        vars = []
1652
1653        if transformed:
1654            _vars = SCIPgetVars(self._scip)
1655            _nvars = SCIPgetNVars(self._scip)
1656        else:
1657            _vars = SCIPgetOrigVars(self._scip)
1658            _nvars = SCIPgetNOrigVars(self._scip)
1659
1660        for i in range(_nvars):
1661            ptr = <size_t>(_vars[i])
1662
1663            # check whether the corresponding variable exists already
1664            if ptr in self._modelvars:
1665                vars.append(self._modelvars[ptr])
1666            else:
1667                # create a new variable
1668                var = Variable.create(_vars[i])
1669                assert var.ptr() == ptr
1670                self._modelvars[ptr] = var
1671                vars.append(var)
1672
1673        return vars
1674
1675    def getNVars(self):
1676        """Retrieve number of variables in the problems"""
1677        return SCIPgetNVars(self._scip)
1678
1679    def getNConss(self):
1680        """Retrieve the number of constraints."""
1681        return SCIPgetNConss(self._scip)
1682
1683    def updateNodeLowerbound(self, Node node, lb):
1684        """if given value is larger than the node's lower bound (in transformed problem),
1685        sets the node's lower bound to the new value
1686
1687        :param node: Node, the node to update
1688        :param newbound: float, new bound (if greater) for the node
1689
1690        """
1691        PY_SCIP_CALL(SCIPupdateNodeLowerbound(self._scip, node.scip_node, lb))
1692
1693    # Node methods
1694    def getBestChild(self):
1695        """gets the best child of the focus node w.r.t. the node selection strategy."""
1696        return Node.create(SCIPgetBestChild(self._scip))
1697
1698    def getBestSibling(self):
1699        """gets the best sibling of the focus node w.r.t. the node selection strategy."""
1700        return Node.create(SCIPgetBestSibling(self._scip))
1701
1702    def getBestLeaf(self):
1703        """gets the best leaf from the node queue w.r.t. the node selection strategy."""
1704        return Node.create(SCIPgetBestLeaf(self._scip))
1705
1706    def getBestNode(self):
1707        """gets the best node from the tree (child, sibling, or leaf) w.r.t. the node selection strategy."""
1708        return Node.create(SCIPgetBestNode(self._scip))
1709
1710    def getBestboundNode(self):
1711        """gets the node with smallest lower bound from the tree (child, sibling, or leaf)."""
1712        return Node.create(SCIPgetBestboundNode(self._scip))
1713
1714    def getOpenNodes(self):
1715        """access to all data of open nodes (leaves, children, and siblings)
1716
1717        :return: three lists containing open leaves, children, siblings
1718        """
1719        cdef SCIP_NODE** _leaves
1720        cdef SCIP_NODE** _children
1721        cdef SCIP_NODE** _siblings
1722        cdef int _nleaves
1723        cdef int _nchildren
1724        cdef int _nsiblings
1725
1726        PY_SCIP_CALL(SCIPgetOpenNodesData(self._scip, &_leaves, &_children, &_siblings, &_nleaves, &_nchildren, &_nsiblings))
1727
1728        leaves   = [Node.create(_leaves[i]) for i in range(_nleaves)]
1729        children = [Node.create(_children[i]) for i in range(_nchildren)]
1730        siblings = [Node.create(_siblings[i]) for i in range(_nsiblings)]
1731
1732        return leaves, children, siblings
1733
1734    def repropagateNode(self, Node node):
1735        """marks the given node to be propagated again the next time a node of its subtree is processed"""
1736        PY_SCIP_CALL(SCIPrepropagateNode(self._scip, node.scip_node))
1737
1738
1739    # LP Methods
1740    def getLPSolstat(self):
1741        """Gets solution status of current LP"""
1742        return SCIPgetLPSolstat(self._scip)
1743
1744
1745    def constructLP(self):
1746        """makes sure that the LP of the current node is loaded and
1747         may be accessed through the LP information methods
1748
1749        :return:  bool cutoff, i.e. can the node be cut off?
1750
1751        """
1752        cdef SCIP_Bool cutoff
1753        PY_SCIP_CALL(SCIPconstructLP(self._scip, &cutoff))
1754        return cutoff
1755
1756    def getLPObjVal(self):
1757        """gets objective value of current LP (which is the sum of column and loose objective value)"""
1758
1759        return SCIPgetLPObjval(self._scip)
1760
1761    def getLPColsData(self):
1762        """Retrieve current LP columns"""
1763        cdef SCIP_COL** cols
1764        cdef int ncols
1765
1766        PY_SCIP_CALL(SCIPgetLPColsData(self._scip, &cols, &ncols))
1767        return [Column.create(cols[i]) for i in range(ncols)]
1768
1769    def getLPRowsData(self):
1770        """Retrieve current LP rows"""
1771        cdef SCIP_ROW** rows
1772        cdef int nrows
1773
1774        PY_SCIP_CALL(SCIPgetLPRowsData(self._scip, &rows, &nrows))
1775        return [Row.create(rows[i]) for i in range(nrows)]
1776
1777    def getNLPRows(self):
1778        """Retrieve the number of rows currently in the LP"""
1779        return SCIPgetNLPRows(self._scip)
1780
1781    def getNLPCols(self):
1782        """Retrieve the number of cols currently in the LP"""
1783        return SCIPgetNLPCols(self._scip)
1784
1785    def getLPBasisInd(self):
1786        """Gets all indices of basic columns and rows: index i >= 0 corresponds to column i, index i < 0 to row -i-1"""
1787        cdef int nrows = SCIPgetNLPRows(self._scip)
1788        cdef int* inds = <int *> malloc(nrows * sizeof(int))
1789
1790        PY_SCIP_CALL(SCIPgetLPBasisInd(self._scip, inds))
1791        result = [inds[i] for i in range(nrows)]
1792        free(inds)
1793        return result
1794
1795    def getLPBInvRow(self, row):
1796        """gets a row from the inverse basis matrix B^-1"""
1797        # TODO: sparsity information
1798        cdef int nrows = SCIPgetNLPRows(self._scip)
1799        cdef SCIP_Real* coefs = <SCIP_Real*> malloc(nrows * sizeof(SCIP_Real))
1800
1801        PY_SCIP_CALL(SCIPgetLPBInvRow(self._scip, row, coefs, NULL, NULL))
1802        result = [coefs[i] for i in range(nrows)]
1803        free(coefs)
1804        return result
1805
1806    def getLPBInvARow(self, row):
1807        """gets a row from B^-1 * A"""
1808        # TODO: sparsity information
1809        cdef int ncols = SCIPgetNLPCols(self._scip)
1810        cdef SCIP_Real* coefs = <SCIP_Real*> malloc(ncols * sizeof(SCIP_Real))
1811
1812        PY_SCIP_CALL(SCIPgetLPBInvARow(self._scip, row, NULL, coefs, NULL, NULL))
1813        result = [coefs[i] for i in range(ncols)]
1814        free(coefs)
1815        return result
1816
1817    def isLPSolBasic(self):
1818        """returns whether the current LP solution is basic, i.e. is defined by a valid simplex basis"""
1819        return SCIPisLPSolBasic(self._scip)
1820
1821    #TODO: documentation!!
1822    # LP Row Methods
1823    def createEmptyRowSepa(self, Sepa sepa, name="row", lhs = 0.0, rhs = None, local = True, modifiable = False, removable = True):
1824        """creates and captures an LP row without any coefficients from a separator
1825
1826        :param sepa: separator that creates the row
1827        :param name: name of row (Default value = "row")
1828        :param lhs: left hand side of row (Default value = 0)
1829        :param rhs: right hand side of row (Default value = None)
1830        :param local: is row only valid locally? (Default value = True)
1831        :param modifiable: is row modifiable during node processing (subject to column generation)? (Default value = False)
1832        :param removable: should the row be removed from the LP due to aging or cleanup? (Default value = True)
1833        """
1834        cdef SCIP_ROW* row
1835        lhs =  -SCIPinfinity(self._scip) if lhs is None else lhs
1836        rhs =  SCIPinfinity(self._scip) if rhs is None else rhs
1837        scip_sepa = SCIPfindSepa(self._scip, str_conversion(sepa.name))
1838        PY_SCIP_CALL(SCIPcreateEmptyRowSepa(self._scip, &row, scip_sepa, str_conversion(name), lhs, rhs, local, modifiable, removable))
1839        PyRow = Row.create(row)
1840        return PyRow
1841
1842    def createEmptyRowUnspec(self, name="row", lhs = 0.0, rhs = None, local = True, modifiable = False, removable = True):
1843        """creates and captures an LP row without any coefficients from an unspecified source
1844
1845        :param name: name of row (Default value = "row")
1846        :param lhs: left hand side of row (Default value = 0)
1847        :param rhs: right hand side of row (Default value = None)
1848        :param local: is row only valid locally? (Default value = True)
1849        :param modifiable: is row modifiable during node processing (subject to column generation)? (Default value = False)
1850        :param removable: should the row be removed from the LP due to aging or cleanup? (Default value = True)
1851        """
1852        cdef SCIP_ROW* row
1853        lhs =  -SCIPinfinity(self._scip) if lhs is None else lhs
1854        rhs =  SCIPinfinity(self._scip) if rhs is None else rhs
1855        PY_SCIP_CALL(SCIPcreateEmptyRowUnspec(self._scip, &row, str_conversion(name), lhs, rhs, local, modifiable, removable))
1856        PyRow = Row.create(row)
1857        return PyRow
1858
1859    def getRowActivity(self, Row row):
1860        """returns the activity of a row in the last LP or pseudo solution"""
1861        return SCIPgetRowActivity(self._scip, row.scip_row)
1862
1863    def getRowLPActivity(self, Row row):
1864        """returns the activity of a row in the last LP solution"""
1865        return SCIPgetRowLPActivity(self._scip, row.scip_row)
1866
1867    # TODO: do we need this? (also do we need release var??)
1868    def releaseRow(self, Row row not None):
1869        """decreases usage counter of LP row, and frees memory if necessary"""
1870        PY_SCIP_CALL(SCIPreleaseRow(self._scip, &row.scip_row))
1871
1872    def cacheRowExtensions(self, Row row not None):
1873        """informs row, that all subsequent additions of variables to the row should be cached and not directly applied;
1874        after all additions were applied, flushRowExtensions() must be called;
1875        while the caching of row extensions is activated, information methods of the row give invalid results;
1876        caching should be used, if a row is build with addVarToRow() calls variable by variable to increase the performance"""
1877        PY_SCIP_CALL(SCIPcacheRowExtensions(self._scip, row.scip_row))
1878
1879    def flushRowExtensions(self, Row row not None):
1880        """flushes all cached row extensions after a call of cacheRowExtensions() and merges coefficients with equal columns into a single coefficient"""
1881        PY_SCIP_CALL(SCIPflushRowExtensions(self._scip, row.scip_row))
1882
1883    def addVarToRow(self, Row row not None, Variable var not None, value):
1884        """resolves variable to columns and adds them with the coefficient to the row"""
1885        PY_SCIP_CALL(SCIPaddVarToRow(self._scip, row.scip_row, var.scip_var, value))
1886
1887    def printRow(self, Row row not None):
1888        """Prints row."""
1889        PY_SCIP_CALL(SCIPprintRow(self._scip, row.scip_row, NULL))
1890
1891    # Cutting Plane Methods
1892    def addPoolCut(self, Row row not None):
1893        """if not already existing, adds row to global cut pool"""
1894        PY_SCIP_CALL(SCIPaddPoolCut(self._scip, row.scip_row))
1895
1896    def getCutEfficacy(self, Row cut not None, Solution sol = None):
1897        """returns efficacy of the cut with respect to the given primal solution or the current LP solution: e = -feasibility/norm"""
1898        return SCIPgetCutEfficacy(self._scip, NULL if sol is None else sol.sol, cut.scip_row)
1899
1900    def isCutEfficacious(self, Row cut not None, Solution sol = None):
1901        """ 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"""
1902        return SCIPisCutEfficacious(self._scip, NULL if sol is None else sol.sol, cut.scip_row)
1903
1904    def addCut(self, Row cut not None, forcecut = False):
1905        """adds cut to separation storage and returns whether cut has been detected to be infeasible for local bounds"""
1906        cdef SCIP_Bool infeasible
1907        PY_SCIP_CALL(SCIPaddRow(self._scip, cut.scip_row, forcecut, &infeasible))
1908        return infeasible
1909
1910    def getNCuts(self):
1911        """Retrieve total number of cuts in storage"""
1912        return SCIPgetNCuts(self._scip)
1913
1914    def getNCutsApplied(self):
1915        """Retrieve number of currently applied cuts"""
1916        return SCIPgetNCutsApplied(self._scip)
1917
1918    def separateSol(self, Solution sol = None, pretendroot = False, allowlocal = True, onlydelayed = False):
1919        """separates the given primal solution or the current LP solution by calling the separators and constraint handlers'
1920        separation methods;
1921        the generated cuts are stored in the separation storage and can be accessed with the methods SCIPgetCuts() and
1922        SCIPgetNCuts();
1923        after evaluating the cuts, you have to call SCIPclearCuts() in order to remove the cuts from the
1924        separation storage;
1925        it is possible to call SCIPseparateSol() multiple times with different solutions and evaluate the found cuts
1926        afterwards
1927        :param Solution sol: solution to separate, None to use current lp solution (Default value = None)
1928        :param pretendroot: should the cut separators be called as if we are at the root node? (Default value = "False")
1929        :param allowlocal: should the separator be asked to separate local cuts (Default value = True)
1930        :param onlydelayed: should only separators be called that were delayed in the previous round? (Default value = False)
1931        returns
1932        delayed -- whether a separator was delayed
1933        cutoff -- whether the node can be cut off
1934        """
1935        cdef SCIP_Bool delayed
1936        cdef SCIP_Bool cutoff
1937
1938        PY_SCIP_CALL( SCIPseparateSol(self._scip, NULL if sol is None else sol.sol, pretendroot, allowlocal, onlydelayed, &delayed, &cutoff) )
1939        return delayed, cutoff
1940
1941    # Constraint functions
1942    def addCons(self, cons, name='', initial=True, separate=True,
1943                enforce=True, check=True, propagate=True, local=False,
1944                modifiable=False, dynamic=False, removable=False,
1945                stickingatnode=False):
1946        """Add a linear or quadratic constraint.
1947
1948        :param cons: list of coefficients
1949        :param name: the name of the constraint, generic name if empty (Default value = '')
1950        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
1951        :param separate: should the constraint be separated during LP processing? (Default value = True)
1952        :param enforce: should the constraint be enforced during node processing? (Default value = True)
1953        :param check: should the constraint be checked during for feasibility? (Default value = True)
1954        :param propagate: should the constraint be propagated during node processing? (Default value = True)
1955        :param local: is the constraint only valid locally? (Default value = False)
1956        :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False)
1957        :param dynamic: is the constraint subject to aging? (Default value = False)
1958        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
1959        :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)
1960
1961        """
1962        assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__
1963
1964        # replace empty name with generic one
1965        if name == '':
1966            name = 'c'+str(SCIPgetNConss(self._scip)+1)
1967
1968        kwargs = dict(name=name, initial=initial, separate=separate,
1969                      enforce=enforce, check=check,
1970                      propagate=propagate, local=local,
1971                      modifiable=modifiable, dynamic=dynamic,
1972                      removable=removable,
1973                      stickingatnode=stickingatnode)
1974        kwargs['lhs'] = -SCIPinfinity(self._scip) if cons._lhs is None else cons._lhs
1975        kwargs['rhs'] =  SCIPinfinity(self._scip) if cons._rhs is None else cons._rhs
1976
1977        deg = cons.expr.degree()
1978        if deg <= 1:
1979            return self._addLinCons(cons, **kwargs)
1980        elif deg <= 2:
1981            return self._addQuadCons(cons, **kwargs)
1982        elif deg == float('inf'): # general nonlinear
1983            return self._addGenNonlinearCons(cons, **kwargs)
1984        else:
1985            return self._addNonlinearCons(cons, **kwargs)
1986
1987    def _addLinCons(self, ExprCons lincons, **kwargs):
1988        assert isinstance(lincons, ExprCons), "given constraint is not ExprCons but %s" % lincons.__class__.__name__
1989
1990        assert lincons.expr.degree() <= 1, "given constraint is not linear, degree == %d" % lincons.expr.degree()
1991        terms = lincons.expr.terms
1992
1993        cdef SCIP_CONS* scip_cons
1994
1995        cdef int nvars = len(terms.items())
1996
1997        vars_array = <SCIP_VAR**> malloc(nvars * sizeof(SCIP_VAR*))
1998        coeffs_array = <SCIP_Real*> malloc(nvars * sizeof(SCIP_Real))
1999
2000        for i, (key, coeff) in enumerate(terms.items()):
2001            vars_array[i] = <SCIP_VAR*>(<Variable>key[0]).scip_var
2002            coeffs_array[i] = <SCIP_Real>coeff
2003
2004        PY_SCIP_CALL(SCIPcreateConsLinear(
2005            self._scip, &scip_cons, str_conversion(kwargs['name']), nvars, vars_array, coeffs_array,
2006            kwargs['lhs'], kwargs['rhs'], kwargs['initial'],
2007            kwargs['separate'], kwargs['enforce'], kwargs['check'],
2008            kwargs['propagate'], kwargs['local'], kwargs['modifiable'],
2009            kwargs['dynamic'], kwargs['removable'], kwargs['stickingatnode']))
2010
2011        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2012        PyCons = Constraint.create(scip_cons)
2013        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2014
2015        free(vars_array)
2016        free(coeffs_array)
2017
2018        return PyCons
2019
2020    def _addQuadCons(self, ExprCons quadcons, **kwargs):
2021        terms = quadcons.expr.terms
2022        assert quadcons.expr.degree() <= 2, "given constraint is not quadratic, degree == %d" % quadcons.expr.degree()
2023
2024        cdef SCIP_CONS* scip_cons
2025        PY_SCIP_CALL(SCIPcreateConsQuadratic(
2026            self._scip, &scip_cons, str_conversion(kwargs['name']),
2027            0, NULL, NULL,        # linear
2028            0, NULL, NULL, NULL,  # quadratc
2029            kwargs['lhs'], kwargs['rhs'],
2030            kwargs['initial'], kwargs['separate'], kwargs['enforce'],
2031            kwargs['check'], kwargs['propagate'], kwargs['local'],
2032            kwargs['modifiable'], kwargs['dynamic'], kwargs['removable']))
2033
2034        for v, c in terms.items():
2035            if len(v) == 1: # linear
2036                var = <Variable>v[0]
2037                PY_SCIP_CALL(SCIPaddLinearVarQuadratic(self._scip, scip_cons, var.scip_var, c))
2038            else: # quadratic
2039                assert len(v) == 2, 'term length must be 1 or 2 but it is %s' % len(v)
2040                var1, var2 = <Variable>v[0], <Variable>v[1]
2041                PY_SCIP_CALL(SCIPaddBilinTermQuadratic(self._scip, scip_cons, var1.scip_var, var2.scip_var, c))
2042
2043        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2044        PyCons = Constraint.create(scip_cons)
2045        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2046        return PyCons
2047
2048    def _addNonlinearCons(self, ExprCons cons, **kwargs):
2049        cdef SCIP_EXPR* expr
2050        cdef SCIP_EXPR** varexprs
2051        cdef SCIP_EXPRDATA_MONOMIAL** monomials
2052        cdef int* idxs
2053        cdef SCIP_EXPRTREE* exprtree
2054        cdef SCIP_VAR** vars
2055        cdef SCIP_CONS* scip_cons
2056
2057        terms = cons.expr.terms
2058
2059        # collect variables
2060        variables = {var.ptr():var for term in terms for var in term}
2061        variables = list(variables.values())
2062        varindex = {var.ptr():idx for (idx,var) in enumerate(variables)}
2063
2064        # create variable expressions
2065        varexprs = <SCIP_EXPR**> malloc(len(varindex) * sizeof(SCIP_EXPR*))
2066        for idx in varindex.values():
2067            PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &expr, SCIP_EXPR_VARIDX, <int>idx) )
2068            varexprs[idx] = expr
2069
2070        # create monomials for terms
2071        monomials = <SCIP_EXPRDATA_MONOMIAL**> malloc(len(terms) * sizeof(SCIP_EXPRDATA_MONOMIAL*))
2072        for i, (term, coef) in enumerate(terms.items()):
2073            idxs = <int*> malloc(len(term) * sizeof(int))
2074            for j, var in enumerate(term):
2075                idxs[j] = varindex[var.ptr()]
2076            PY_SCIP_CALL( SCIPexprCreateMonomial(SCIPblkmem(self._scip), &monomials[i], <SCIP_Real>coef, <int>len(term), idxs, NULL) )
2077            free(idxs)
2078
2079        # create polynomial from monomials
2080        PY_SCIP_CALL( SCIPexprCreatePolynomial(SCIPblkmem(self._scip), &expr,
2081                                               <int>len(varindex), varexprs,
2082                                               <int>len(terms), monomials, 0.0, <SCIP_Bool>True) )
2083
2084        # create expression tree
2085        PY_SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(self._scip), &exprtree, expr, <int>len(variables), 0, NULL) )
2086        vars = <SCIP_VAR**> malloc(len(variables) * sizeof(SCIP_VAR*))
2087        for idx, var in enumerate(variables): # same as varindex
2088            vars[idx] = (<Variable>var).scip_var
2089        PY_SCIP_CALL( SCIPexprtreeSetVars(exprtree, <int>len(variables), vars) )
2090
2091        # create nonlinear constraint for exprtree
2092        PY_SCIP_CALL( SCIPcreateConsNonlinear(
2093            self._scip, &scip_cons, str_conversion(kwargs['name']),
2094            0, NULL, NULL, # linear
2095            1, &exprtree, NULL, # nonlinear
2096            kwargs['lhs'], kwargs['rhs'],
2097            kwargs['initial'], kwargs['separate'], kwargs['enforce'],
2098            kwargs['check'], kwargs['propagate'], kwargs['local'],
2099            kwargs['modifiable'], kwargs['dynamic'], kwargs['removable'],
2100            kwargs['stickingatnode']) )
2101        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2102        PyCons = Constraint.create(scip_cons)
2103        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2104        PY_SCIP_CALL( SCIPexprtreeFree(&exprtree) )
2105        free(vars)
2106        free(monomials)
2107        free(varexprs)
2108        return PyCons
2109
2110    def _addGenNonlinearCons(self, ExprCons cons, **kwargs):
2111        cdef SCIP_EXPR** childrenexpr
2112        cdef SCIP_EXPR** scipexprs
2113        cdef SCIP_EXPRTREE* exprtree
2114        cdef SCIP_CONS* scip_cons
2115        cdef int nchildren
2116
2117        # get arrays from python's expression tree
2118        expr = cons.expr
2119        nodes = expr_to_nodes(expr)
2120        op2idx = Operator.operatorIndexDic
2121
2122        # in nodes we have a list of tuples: each tuple is of the form
2123        # (operator, [indices]) where indices are the indices of the tuples
2124        # that are the children of this operator. This is sorted,
2125        # so we are going to do is:
2126        # loop over the nodes and create the expression of each
2127        # Note1: when the operator is SCIP_EXPR_CONST, [indices] stores the value
2128        # Note2: we need to compute the number of variable operators to find out
2129        # how many variables are there.
2130        nvars = 0
2131        for node in nodes:
2132            if op2idx[node[0]] == SCIP_EXPR_VARIDX:
2133                nvars += 1
2134        vars = <SCIP_VAR**> malloc(nvars * sizeof(SCIP_VAR*))
2135
2136        varpos = 0
2137        scipexprs = <SCIP_EXPR**> malloc(len(nodes) * sizeof(SCIP_EXPR*))
2138        for i,node in enumerate(nodes):
2139            op = node[0]
2140            opidx = op2idx[op]
2141            if opidx == SCIP_EXPR_VARIDX:
2142                assert len(node[1]) == 1
2143                pyvar = node[1][0] # for vars we store the actual var!
2144                PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], opidx, <int>varpos) )
2145                vars[varpos] = (<Variable>pyvar).scip_var
2146                varpos += 1
2147                continue
2148            if opidx == SCIP_EXPR_CONST:
2149                assert len(node[1]) == 1
2150                value = node[1][0]
2151                PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], opidx, <SCIP_Real>value) )
2152                continue
2153            if opidx == SCIP_EXPR_SUM or opidx == SCIP_EXPR_PRODUCT:
2154                nchildren = len(node[1])
2155                childrenexpr = <SCIP_EXPR**> malloc(nchildren * sizeof(SCIP_EXPR*))
2156                for c, pos in enumerate(node[1]):
2157                    childrenexpr[c] = scipexprs[pos]
2158                PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], opidx, nchildren, childrenexpr) )
2159
2160                free(childrenexpr)
2161                continue
2162            if opidx == SCIP_EXPR_REALPOWER:
2163                # the second child is the exponent which is a const
2164                valuenode = nodes[node[1][1]]
2165                assert op2idx[valuenode[0]] == SCIP_EXPR_CONST
2166                exponent = valuenode[1][0]
2167                if float(exponent).is_integer():
2168                    PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], SCIP_EXPR_INTPOWER, scipexprs[node[1][0]], <int>exponent) )
2169                else:
2170                    PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], opidx, scipexprs[node[1][0]], <SCIP_Real>exponent) )
2171                continue
2172            if opidx == SCIP_EXPR_EXP or opidx == SCIP_EXPR_LOG or opidx == SCIP_EXPR_SQRT or opidx == SCIP_EXPR_ABS:
2173                assert len(node[1]) == 1
2174                PY_SCIP_CALL( SCIPexprCreate(SCIPblkmem(self._scip), &scipexprs[i], opidx, scipexprs[node[1][0]]) )
2175                continue
2176            # default:
2177            raise NotImplementedError
2178        assert varpos == nvars
2179
2180        # create expression tree
2181        PY_SCIP_CALL( SCIPexprtreeCreate(SCIPblkmem(self._scip), &exprtree, scipexprs[len(nodes) - 1], nvars, 0, NULL) )
2182        PY_SCIP_CALL( SCIPexprtreeSetVars(exprtree, <int>nvars, vars) )
2183
2184        # create nonlinear constraint for exprtree
2185        PY_SCIP_CALL( SCIPcreateConsNonlinear(
2186            self._scip, &scip_cons, str_conversion(kwargs['name']),
2187            0, NULL, NULL, # linear
2188            1, &exprtree, NULL, # nonlinear
2189            kwargs['lhs'], kwargs['rhs'],
2190            kwargs['initial'], kwargs['separate'], kwargs['enforce'],
2191            kwargs['check'], kwargs['propagate'], kwargs['local'],
2192            kwargs['modifiable'], kwargs['dynamic'], kwargs['removable'],
2193            kwargs['stickingatnode']) )
2194        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2195        PyCons = Constraint.create(scip_cons)
2196        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2197        PY_SCIP_CALL( SCIPexprtreeFree(&exprtree) )
2198
2199        # free more memory
2200        free(scipexprs)
2201        free(vars)
2202
2203        return PyCons
2204
2205    def addConsCoeff(self, Constraint cons, Variable var, coeff):
2206        """Add coefficient to the linear constraint (if non-zero).
2207
2208        :param Constraint cons: constraint to be changed
2209        :param Variable var: variable to be added
2210        :param coeff: coefficient of new variable
2211
2212        """
2213        PY_SCIP_CALL(SCIPaddCoefLinear(self._scip, cons.scip_cons, var.scip_var, coeff))
2214
2215    def addConsNode(self, Node node, Constraint cons, Node validnode=None):
2216        """Add a constraint to the given node
2217
2218        :param Node node: node to add the constraint to
2219        :param Constraint cons: constraint to add
2220        :param Node validnode: more global node where cons is also valid
2221
2222        """
2223        if isinstance(validnode, Node):
2224            PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node, cons.scip_cons, validnode.scip_node))
2225        else:
2226            PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node, cons.scip_cons, NULL))
2227        Py_INCREF(cons)
2228
2229    def addConsLocal(self, Constraint cons, Node validnode=None):
2230        """Add a constraint to the current node
2231
2232        :param Constraint cons: constraint to add
2233        :param Node validnode: more global node where cons is also valid
2234
2235        """
2236        if isinstance(validnode, Node):
2237            PY_SCIP_CALL(SCIPaddConsLocal(self._scip, cons.scip_cons, validnode.scip_node))
2238        else:
2239            PY_SCIP_CALL(SCIPaddConsLocal(self._scip, cons.scip_cons, NULL))
2240        Py_INCREF(cons)
2241
2242    def addConsSOS1(self, vars, weights=None, name="SOS1cons",
2243                initial=True, separate=True, enforce=True, check=True,
2244                propagate=True, local=False, dynamic=False,
2245                removable=False, stickingatnode=False):
2246        """Add an SOS1 constraint.
2247
2248        :param vars: list of variables to be included
2249        :param weights: list of weights (Default value = None)
2250        :param name: name of the constraint (Default value = "SOS1cons")
2251        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2252        :param separate: should the constraint be separated during LP processing? (Default value = True)
2253        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2254        :param check: should the constraint be checked for feasibility? (Default value = True)
2255        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2256        :param local: is the constraint only valid locally? (Default value = False)
2257        :param dynamic: is the constraint subject to aging? (Default value = False)
2258        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2259        :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)
2260
2261        """
2262        cdef SCIP_CONS* scip_cons
2263        cdef int _nvars
2264
2265        PY_SCIP_CALL(SCIPcreateConsSOS1(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL,
2266            initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode))
2267
2268        if weights is None:
2269            for v in vars:
2270                var = <Variable>v
2271                PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, scip_cons, var.scip_var))
2272        else:
2273            nvars = len(vars)
2274            for i in range(nvars):
2275                var = <Variable>vars[i]
2276                PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, scip_cons, var.scip_var, weights[i]))
2277
2278        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2279        return Constraint.create(scip_cons)
2280
2281    def addConsSOS2(self, vars, weights=None, name="SOS2cons",
2282                initial=True, separate=True, enforce=True, check=True,
2283                propagate=True, local=False, dynamic=False,
2284                removable=False, stickingatnode=False):
2285        """Add an SOS2 constraint.
2286
2287        :param vars: list of variables to be included
2288        :param weights: list of weights (Default value = None)
2289        :param name: name of the constraint (Default value = "SOS2cons")
2290        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2291        :param separate: should the constraint be separated during LP processing? (Default value = True)
2292        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2293        :param check: should the constraint be checked for feasibility? (Default value = True)
2294        :param propagate: is the constraint only valid locally? (Default value = True)
2295        :param local: is the constraint only valid locally? (Default value = False)
2296        :param dynamic: is the constraint subject to aging? (Default value = False)
2297        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2298        :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)
2299
2300        """
2301        cdef SCIP_CONS* scip_cons
2302        cdef int _nvars
2303
2304        PY_SCIP_CALL(SCIPcreateConsSOS2(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL,
2305            initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode))
2306
2307        if weights is None:
2308            for v in vars:
2309                var = <Variable>v
2310                PY_SCIP_CALL(SCIPappendVarSOS2(self._scip, scip_cons, var.scip_var))
2311        else:
2312            nvars = len(vars)
2313            for i in range(nvars):
2314                var = <Variable>vars[i]
2315                PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, scip_cons, var.scip_var, weights[i]))
2316
2317        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2318        return Constraint.create(scip_cons)
2319
2320    def addConsAnd(self, vars, resvar, name="ANDcons",
2321            initial=True, separate=True, enforce=True, check=True,
2322            propagate=True, local=False, modifiable=False, dynamic=False,
2323            removable=False, stickingatnode=False):
2324        """Add an AND-constraint.
2325        :param vars: list of BINARY variables to be included (operators)
2326        :param resvar: BINARY variable (resultant)
2327        :param name: name of the constraint (Default value = "ANDcons")
2328        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2329        :param separate: should the constraint be separated during LP processing? (Default value = True)
2330        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2331        :param check: should the constraint be checked for feasibility? (Default value = True)
2332        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2333        :param local: is the constraint only valid locally? (Default value = False)
2334        :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False)
2335        :param dynamic: is the constraint subject to aging? (Default value = False)
2336        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2337        :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)
2338        """
2339        cdef SCIP_CONS* scip_cons
2340
2341        nvars = len(vars)
2342
2343        _vars = <SCIP_VAR**> malloc(len(vars) * sizeof(SCIP_VAR*))
2344        for idx, var in enumerate(vars):
2345            _vars[idx] = (<Variable>var).scip_var
2346        _resVar = (<Variable>resvar).scip_var
2347
2348        PY_SCIP_CALL(SCIPcreateConsAnd(self._scip, &scip_cons, str_conversion(name), _resVar, nvars, _vars,
2349            initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
2350
2351        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2352        pyCons = Constraint.create(scip_cons)
2353        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2354
2355        free(_vars)
2356
2357        return pyCons
2358
2359    def addConsOr(self, vars, resvar, name="ORcons",
2360            initial=True, separate=True, enforce=True, check=True,
2361            propagate=True, local=False, modifiable=False, dynamic=False,
2362            removable=False, stickingatnode=False):
2363        """Add an OR-constraint.
2364        :param vars: list of BINARY variables to be included (operators)
2365        :param resvar: BINARY variable (resultant)
2366        :param name: name of the constraint (Default value = "ORcons")
2367        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2368        :param separate: should the constraint be separated during LP processing? (Default value = True)
2369        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2370        :param check: should the constraint be checked for feasibility? (Default value = True)
2371        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2372        :param local: is the constraint only valid locally? (Default value = False)
2373        :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False)
2374        :param dynamic: is the constraint subject to aging? (Default value = False)
2375        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2376        :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)
2377        """
2378        cdef SCIP_CONS* scip_cons
2379
2380        nvars = len(vars)
2381
2382        _vars = <SCIP_VAR**> malloc(len(vars) * sizeof(SCIP_VAR*))
2383        for idx, var in enumerate(vars):
2384            _vars[idx] = (<Variable>var).scip_var
2385        _resVar = (<Variable>resvar).scip_var
2386
2387        PY_SCIP_CALL(SCIPcreateConsOr(self._scip, &scip_cons, str_conversion(name), _resVar, nvars, _vars,
2388            initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
2389
2390        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2391        pyCons = Constraint.create(scip_cons)
2392        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2393
2394        free(_vars)
2395
2396        return pyCons
2397
2398    def addConsXor(self, vars, rhsvar, name="XORcons",
2399            initial=True, separate=True, enforce=True, check=True,
2400            propagate=True, local=False, modifiable=False, dynamic=False,
2401            removable=False, stickingatnode=False):
2402        """Add a XOR-constraint.
2403        :param vars: list of BINARY variables to be included (operators)
2404        :param rhsvar: BOOLEAN value, explicit True, False or bool(obj) is needed (right-hand side)
2405        :param name: name of the constraint (Default value = "XORcons")
2406        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2407        :param separate: should the constraint be separated during LP processing? (Default value = True)
2408        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2409        :param check: should the constraint be checked for feasibility? (Default value = True)
2410        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2411        :param local: is the constraint only valid locally? (Default value = False)
2412        :param modifiable: is the constraint modifiable (subject to column generation)? (Default value = False)
2413        :param dynamic: is the constraint subject to aging? (Default value = False)
2414        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2415        :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)
2416        """
2417        cdef SCIP_CONS* scip_cons
2418
2419        nvars = len(vars)
2420
2421        assert type(rhsvar) is type(bool()), "Provide BOOLEAN value as rhsvar, you gave %s." % type(rhsvar)
2422        _vars = <SCIP_VAR**> malloc(len(vars) * sizeof(SCIP_VAR*))
2423        for idx, var in enumerate(vars):
2424            _vars[idx] = (<Variable>var).scip_var
2425
2426        PY_SCIP_CALL(SCIPcreateConsXor(self._scip, &scip_cons, str_conversion(name), rhsvar, nvars, _vars,
2427            initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
2428
2429        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2430        pyCons = Constraint.create(scip_cons)
2431        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2432
2433        free(_vars)
2434
2435        return pyCons
2436
2437    def addConsCardinality(self, consvars, cardval, indvars=None, weights=None, name="CardinalityCons",
2438                initial=True, separate=True, enforce=True, check=True,
2439                propagate=True, local=False, dynamic=False,
2440                removable=False, stickingatnode=False):
2441        """Add a cardinality constraint that allows at most 'cardval' many nonzero variables.
2442
2443        :param consvars: list of variables to be included
2444        :param cardval: nonnegative integer
2445        :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)
2446        :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)
2447        :param name: name of the constraint (Default value = "CardinalityCons")
2448        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2449        :param separate: should the constraint be separated during LP processing? (Default value = True)
2450        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2451        :param check: should the constraint be checked for feasibility? (Default value = True)
2452        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2453        :param local: is the constraint only valid locally? (Default value = False)
2454        :param dynamic: is the constraint subject to aging? (Default value = False)
2455        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2456        :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)
2457
2458        """
2459        cdef SCIP_CONS* scip_cons
2460        cdef SCIP_VAR* indvar
2461
2462        PY_SCIP_CALL(SCIPcreateConsCardinality(self._scip, &scip_cons, str_conversion(name), 0, NULL, cardval, NULL, NULL,
2463            initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode))
2464
2465        # circumvent an annoying bug in SCIP 4.0.0 that does not allow uninitialized weights
2466        if weights is None:
2467            weights = list(range(1, len(consvars) + 1))
2468
2469        for i, v in enumerate(consvars):
2470            var = <Variable>v
2471            if indvars:
2472                indvar = (<Variable>indvars[i]).scip_var
2473            else:
2474                indvar = NULL
2475            if weights is None:
2476                PY_SCIP_CALL(SCIPappendVarCardinality(self._scip, scip_cons, var.scip_var, indvar))
2477            else:
2478                PY_SCIP_CALL(SCIPaddVarCardinality(self._scip, scip_cons, var.scip_var, indvar, <SCIP_Real>weights[i]))
2479
2480        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2481        pyCons = Constraint.create(scip_cons)
2482
2483        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2484
2485        return pyCons
2486
2487
2488    def addConsIndicator(self, cons, binvar=None, name="IndicatorCons",
2489                initial=True, separate=True, enforce=True, check=True,
2490                propagate=True, local=False, dynamic=False,
2491                removable=False, stickingatnode=False):
2492        """Add an indicator constraint for the linear inequality 'cons'.
2493
2494        The 'binvar' argument models the redundancy of the linear constraint. A solution for which
2495        'binvar' is 1 must satisfy the constraint.
2496
2497        :param cons: a linear inequality of the form "<="
2498        :param binvar: binary indicator variable, or None if it should be created (Default value = None)
2499        :param name: name of the constraint (Default value = "IndicatorCons")
2500        :param initial: should the LP relaxation of constraint be in the initial LP? (Default value = True)
2501        :param separate: should the constraint be separated during LP processing? (Default value = True)
2502        :param enforce: should the constraint be enforced during node processing? (Default value = True)
2503        :param check: should the constraint be checked for feasibility? (Default value = True)
2504        :param propagate: should the constraint be propagated during node processing? (Default value = True)
2505        :param local: is the constraint only valid locally? (Default value = False)
2506        :param dynamic: is the constraint subject to aging? (Default value = False)
2507        :param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2508        :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)
2509
2510        """
2511        assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__
2512        cdef SCIP_CONS* scip_cons
2513        cdef SCIP_VAR* _binVar
2514        if cons._lhs is not None and cons._rhs is not None:
2515            raise ValueError("expected inequality that has either only a left or right hand side")
2516
2517        if cons.expr.degree() > 1:
2518            raise ValueError("expected linear inequality, expression has degree %d" % cons.expr.degree())
2519
2520
2521        if cons._rhs is not None:
2522            rhs =  cons._rhs
2523            negate = False
2524        else:
2525            rhs = -cons._lhs
2526            negate = True
2527
2528        _binVar = (<Variable>binvar).scip_var if binvar is not None else NULL
2529
2530        PY_SCIP_CALL(SCIPcreateConsIndicator(self._scip, &scip_cons, str_conversion(name), _binVar, 0, NULL, NULL, rhs,
2531            initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode))
2532        terms = cons.expr.terms
2533
2534        for key, coeff in terms.items():
2535            var = <Variable>key[0]
2536            if negate:
2537                coeff = -coeff
2538            PY_SCIP_CALL(SCIPaddVarIndicator(self._scip, scip_cons, var.scip_var, <SCIP_Real>coeff))
2539
2540        PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
2541        pyCons = Constraint.create(scip_cons)
2542
2543        PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2544
2545        return pyCons
2546
2547    def addPyCons(self, Constraint cons):
2548        """Adds a customly created cons.
2549
2550        :param Constraint cons: constraint to add
2551
2552        """
2553        PY_SCIP_CALL(SCIPaddCons(self._scip, cons.scip_cons))
2554        Py_INCREF(cons)
2555
2556    def addVarSOS1(self, Constraint cons, Variable var, weight):
2557        """Add variable to SOS1 constraint.
2558
2559        :param Constraint cons: SOS1 constraint
2560        :param Variable var: new variable
2561        :param weight: weight of new variable
2562
2563        """
2564        PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, cons.scip_cons, var.scip_var, weight))
2565
2566    def appendVarSOS1(self, Constraint cons, Variable var):
2567        """Append variable to SOS1 constraint.
2568
2569        :param Constraint cons: SOS1 constraint
2570        :param Variable var: variable to append
2571
2572        """
2573        PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, cons.scip_cons, var.scip_var))
2574
2575    def addVarSOS2(self, Constraint cons, Variable var, weight):
2576        """Add variable to SOS2 constraint.
2577
2578        :param Constraint cons: SOS2 constraint
2579        :param Variable var: new variable
2580        :param weight: weight of new variable
2581
2582        """
2583        PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, cons.scip_cons, var.scip_var, weight))
2584
2585    def appendVarSOS2(self, Constraint cons, Variable var):
2586        """Append variable to SOS2 constraint.
2587
2588        :param Constraint cons: SOS2 constraint
2589        :param Variable var: variable to append
2590
2591        """
2592        PY_SCIP_CALL(SCIPappendVarSOS2(self._scip, cons.scip_cons, var.scip_var))
2593
2594    def setInitial(self, Constraint cons, newInit):
2595        """Set "initial" flag of a constraint.
2596
2597        Keyword arguments:
2598        cons -- constraint
2599        newInit -- new initial value
2600        """
2601        PY_SCIP_CALL(SCIPsetConsInitial(self._scip, cons.scip_cons, newInit))
2602
2603    def setRemovable(self, Constraint cons, newRem):
2604        """Set "removable" flag of a constraint.
2605
2606        Keyword arguments:
2607        cons -- constraint
2608        newRem -- new removable value
2609        """
2610        PY_SCIP_CALL(SCIPsetConsRemovable(self._scip, cons.scip_cons, newRem))
2611
2612    def setEnforced(self, Constraint cons, newEnf):
2613        """Set "enforced" flag of a constraint.
2614
2615        Keyword arguments:
2616        cons -- constraint
2617        newEnf -- new enforced value
2618        """
2619        PY_SCIP_CALL(SCIPsetConsEnforced(self._scip, cons.scip_cons, newEnf))
2620
2621    def setCheck(self, Constraint cons, newCheck):
2622        """Set "check" flag of a constraint.
2623
2624        Keyword arguments:
2625        cons -- constraint
2626        newCheck -- new check value
2627        """
2628        PY_SCIP_CALL(SCIPsetConsChecked(self._scip, cons.scip_cons, newCheck))
2629
2630    def chgRhs(self, Constraint cons, rhs):
2631        """Change right hand side value of a constraint.
2632
2633        :param Constraint cons: linear or quadratic constraint
2634        :param rhs: new ride hand side (set to None for +infinity)
2635
2636        """
2637
2638        if rhs is None:
2639           rhs = SCIPinfinity(self._scip)
2640
2641        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2642        if constype == 'linear':
2643            PY_SCIP_CALL(SCIPchgRhsLinear(self._scip, cons.scip_cons, rhs))
2644        elif constype == 'quadratic':
2645            PY_SCIP_CALL(SCIPchgRhsQuadratic(self._scip, cons.scip_cons, rhs))
2646        else:
2647            raise Warning("method cannot be called for constraints of type " + constype)
2648
2649    def chgLhs(self, Constraint cons, lhs):
2650        """Change left hand side value of a constraint.
2651
2652        :param Constraint cons: linear or quadratic constraint
2653        :param lhs: new left hand side (set to None for -infinity)
2654
2655        """
2656
2657        if lhs is None:
2658           lhs = -SCIPinfinity(self._scip)
2659
2660        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2661        if constype == 'linear':
2662            PY_SCIP_CALL(SCIPchgLhsLinear(self._scip, cons.scip_cons, lhs))
2663        elif constype == 'quadratic':
2664            PY_SCIP_CALL(SCIPchgLhsQuadratic(self._scip, cons.scip_cons, lhs))
2665        else:
2666            raise Warning("method cannot be called for constraints of type " + constype)
2667
2668    def getRhs(self, Constraint cons):
2669        """Retrieve right hand side value of a constraint.
2670
2671        :param Constraint cons: linear or quadratic constraint
2672
2673        """
2674        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2675        if constype == 'linear':
2676            return SCIPgetRhsLinear(self._scip, cons.scip_cons)
2677        elif constype == 'quadratic':
2678            return SCIPgetRhsQuadratic(self._scip, cons.scip_cons)
2679        else:
2680            raise Warning("method cannot be called for constraints of type " + constype)
2681
2682    def getLhs(self, Constraint cons):
2683        """Retrieve left hand side value of a constraint.
2684
2685        :param Constraint cons: linear or quadratic constraint
2686
2687        """
2688        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2689        if constype == 'linear':
2690            return SCIPgetLhsLinear(self._scip, cons.scip_cons)
2691        elif constype == 'quadratic':
2692            return SCIPgetLhsQuadratic(self._scip, cons.scip_cons)
2693        else:
2694            raise Warning("method cannot be called for constraints of type " + constype)
2695
2696    def getActivity(self, Constraint cons, Solution sol = None):
2697        """Retrieve activity of given constraint.
2698        Can only be called after solving is completed.
2699
2700        :param Constraint cons: linear or quadratic constraint
2701        :param Solution sol: solution to compute activity of, None to use current node's solution (Default value = None)
2702
2703        """
2704        cdef SCIP_Real activity
2705        cdef SCIP_SOL* scip_sol
2706
2707        if not self.getStage() >= SCIP_STAGE_SOLVING:
2708            raise Warning("method cannot be called before problem is solved")
2709
2710        if isinstance(sol, Solution):
2711            scip_sol = sol.sol
2712        else:
2713            scip_sol = NULL
2714
2715        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2716        if constype == 'linear':
2717            activity = SCIPgetActivityLinear(self._scip, cons.scip_cons, scip_sol)
2718        elif constype == 'quadratic':
2719            PY_SCIP_CALL(SCIPgetActivityQuadratic(self._scip, cons.scip_cons, scip_sol, &activity))
2720        else:
2721            raise Warning("method cannot be called for constraints of type " + constype)
2722
2723        return activity
2724
2725
2726    def getSlack(self, Constraint cons, Solution sol = None, side = None):
2727        """Retrieve slack of given constraint.
2728        Can only be called after solving is completed.
2729
2730
2731        :param Constraint cons: linear or quadratic constraint
2732        :param Solution sol: solution to compute slack of, None to use current node's solution (Default value = None)
2733        :param side: whether to use 'lhs' or 'rhs' for ranged constraints, None to return minimum (Default value = None)
2734
2735        """
2736        cdef SCIP_Real activity
2737        cdef SCIP_SOL* scip_sol
2738
2739
2740        if not self.getStage() >= SCIP_STAGE_SOLVING:
2741            raise Warning("method cannot be called before problem is solved")
2742
2743        if isinstance(sol, Solution):
2744            scip_sol = sol.sol
2745        else:
2746            scip_sol = NULL
2747
2748        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2749        if constype == 'linear':
2750            lhs = SCIPgetLhsLinear(self._scip, cons.scip_cons)
2751            rhs = SCIPgetRhsLinear(self._scip, cons.scip_cons)
2752            activity = SCIPgetActivityLinear(self._scip, cons.scip_cons, scip_sol)
2753        elif constype == 'quadratic':
2754            lhs = SCIPgetLhsQuadratic(self._scip, cons.scip_cons)
2755            rhs = SCIPgetRhsQuadratic(self._scip, cons.scip_cons)
2756            PY_SCIP_CALL(SCIPgetActivityQuadratic(self._scip, cons.scip_cons, scip_sol, &activity))
2757        else:
2758            raise Warning("method cannot be called for constraints of type " + constype)
2759
2760        lhsslack = activity - lhs
2761        rhsslack = rhs - activity
2762
2763        if side == 'lhs':
2764            return lhsslack
2765        elif side == 'rhs':
2766            return rhsslack
2767        else:
2768            return min(lhsslack, rhsslack)
2769
2770    def getTransformedCons(self, Constraint cons):
2771        """Retrieve transformed constraint.
2772
2773        :param Constraint cons: constraint
2774
2775        """
2776        cdef SCIP_CONS* transcons
2777        PY_SCIP_CALL(SCIPgetTransformedCons(self._scip, cons.scip_cons, &transcons))
2778        return Constraint.create(transcons)
2779
2780    def isNLPConstructed(self):
2781        """returns whether SCIP's internal NLP has been constructed"""
2782        return SCIPisNLPConstructed(self._scip)
2783
2784    def getNNlRows(self):
2785        """gets current number of nonlinear rows in SCIP's internal NLP"""
2786        return SCIPgetNNLPNlRows(self._scip)
2787
2788    def getNlRows(self):
2789        """returns a list with the nonlinear rows in SCIP's internal NLP"""
2790        cdef SCIP_NLROW** nlrows
2791
2792        nlrows = SCIPgetNLPNlRows(self._scip)
2793        return [NLRow.create(nlrows[i]) for i in range(self.getNNlRows())]
2794
2795    def getNlRowSolActivity(self, NLRow nlrow, Solution sol = None):
2796        """gives the activity of a nonlinear row for a given primal solution
2797        Keyword arguments:
2798        nlrow -- nonlinear row
2799        solution -- a primal solution, if None, then the current LP solution is used
2800        """
2801        cdef SCIP_Real activity
2802        cdef SCIP_SOL* solptr
2803
2804        solptr = sol.sol if not sol is None else NULL
2805        PY_SCIP_CALL( SCIPgetNlRowSolActivity(self._scip, nlrow.scip_nlrow, solptr, &activity) )
2806        return activity
2807
2808    def getNlRowSolFeasibility(self, NLRow nlrow, Solution sol = None):
2809        """gives the feasibility of a nonlinear row for a given primal solution
2810        Keyword arguments:
2811        nlrow -- nonlinear row
2812        solution -- a primal solution, if None, then the current LP solution is used
2813        """
2814        cdef SCIP_Real feasibility
2815        cdef SCIP_SOL* solptr
2816
2817        solptr = sol.sol if not sol is None else NULL
2818        PY_SCIP_CALL( SCIPgetNlRowSolFeasibility(self._scip, nlrow.scip_nlrow, solptr, &feasibility) )
2819        return feasibility
2820
2821    def getNlRowActivityBounds(self, NLRow nlrow):
2822        """gives the minimal and maximal activity of a nonlinear row w.r.t. the variable's bounds"""
2823        cdef SCIP_Real minactivity
2824        cdef SCIP_Real maxactivity
2825
2826        PY_SCIP_CALL( SCIPgetNlRowActivityBounds(self._scip, nlrow.scip_nlrow, &minactivity, &maxactivity) )
2827        return (minactivity, maxactivity)
2828
2829    def printNlRow(self, NLRow nlrow):
2830        """prints nonlinear row"""
2831        PY_SCIP_CALL( SCIPprintNlRow(self._scip, nlrow.scip_nlrow, NULL) )
2832
2833    def getTermsQuadratic(self, Constraint cons):
2834        """Retrieve bilinear, quadratic, and linear terms of a quadratic constraint.
2835
2836        :param Constraint cons: constraint
2837
2838        """
2839        cdef SCIP_QUADVARTERM* _quadterms
2840        cdef SCIP_BILINTERM* _bilinterms
2841        cdef SCIP_VAR** _linvars
2842        cdef SCIP_Real* _lincoefs
2843        cdef int _nbilinterms
2844        cdef int _nquadterms
2845        cdef int _nlinvars
2846
2847        assert cons.isQuadratic(), "constraint is not quadratic"
2848
2849        bilinterms = []
2850        quadterms  = []
2851        linterms   = []
2852
2853        # bilinear terms
2854        _bilinterms = SCIPgetBilinTermsQuadratic(self._scip, cons.scip_cons)
2855        _nbilinterms = SCIPgetNBilinTermsQuadratic(self._scip, cons.scip_cons)
2856
2857        for i in range(_nbilinterms):
2858            var1 = Variable.create(_bilinterms[i].var1)
2859            var2 = Variable.create(_bilinterms[i].var2)
2860            bilinterms.append((var1,var2,_bilinterms[i].coef))
2861
2862        # quadratic terms
2863        _quadterms = SCIPgetQuadVarTermsQuadratic(self._scip, cons.scip_cons)
2864        _nquadterms = SCIPgetNQuadVarTermsQuadratic(self._scip, cons.scip_cons)
2865
2866        for i in range(_nquadterms):
2867            var = Variable.create(_quadterms[i].var)
2868            quadterms.append((var,_quadterms[i].sqrcoef,_quadterms[i].lincoef))
2869
2870        # linear terms
2871        _linvars = SCIPgetLinearVarsQuadratic(self._scip, cons.scip_cons)
2872        _lincoefs = SCIPgetCoefsLinearVarsQuadratic(self._scip, cons.scip_cons)
2873        _nlinvars = SCIPgetNLinearVarsQuadratic(self._scip, cons.scip_cons)
2874
2875        for i in range(_nlinvars):
2876            var = Variable.create(_linvars[i])
2877            linterms.append((var,_lincoefs[i]))
2878
2879        return (bilinterms, quadterms, linterms)
2880
2881    def setRelaxSolVal(self, Variable var, val):
2882        """sets the value of the given variable in the global relaxation solution"""
2883        PY_SCIP_CALL(SCIPsetRelaxSolVal(self._scip, NULL, var.scip_var, val))
2884
2885    def getConss(self):
2886        """Retrieve all constraints."""
2887        cdef SCIP_CONS** _conss
2888        cdef int _nconss
2889        conss = []
2890
2891        _conss = SCIPgetConss(self._scip)
2892        _nconss = SCIPgetNConss(self._scip)
2893        return [Constraint.create(_conss[i]) for i in range(_nconss)]
2894
2895    def getNConss(self):
2896        """Retrieve number of all constraints"""
2897        return SCIPgetNConss(self._scip)
2898
2899    def delCons(self, Constraint cons):
2900        """Delete constraint from the model
2901
2902        :param Constraint cons: constraint to be deleted
2903
2904        """
2905        PY_SCIP_CALL(SCIPdelCons(self._scip, cons.scip_cons))
2906
2907    def delConsLocal(self, Constraint cons):
2908        """Delete constraint from the current node and it's children
2909
2910        :param Constraint cons: constraint to be deleted
2911
2912        """
2913        PY_SCIP_CALL(SCIPdelConsLocal(self._scip, cons.scip_cons))
2914
2915    def getValsLinear(self, Constraint cons):
2916        """Retrieve the coefficients of a linear constraint
2917
2918        :param Constraint cons: linear constraint to get the coefficients of
2919
2920        """
2921        cdef SCIP_Real* _vals
2922        cdef SCIP_VAR** _vars
2923
2924        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2925        if not constype == 'linear':
2926            raise Warning("coefficients not available for constraints of type ", constype)
2927
2928        _vals = SCIPgetValsLinear(self._scip, cons.scip_cons)
2929        _vars = SCIPgetVarsLinear(self._scip, cons.scip_cons)
2930
2931        valsdict = {}
2932        for i in range(SCIPgetNVarsLinear(self._scip, cons.scip_cons)):
2933            valsdict[bytes(SCIPvarGetName(_vars[i])).decode('utf-8')] = _vals[i]
2934        return valsdict
2935
2936    def getDualsolLinear(self, Constraint cons):
2937        """Retrieve the dual solution to a linear constraint.
2938
2939        :param Constraint cons: linear constraint
2940
2941        """
2942        constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
2943        if not constype == 'linear':
2944            raise Warning("dual solution values not available for constraints of type ", constype)
2945        if cons.isOriginal():
2946            transcons = <Constraint>self.getTransformedCons(cons)
2947        else:
2948            transcons = cons
2949        return SCIPgetDualsolLinear(self._scip, transcons.scip_cons)
2950
2951    def getDualMultiplier(self, Constraint cons):
2952        """DEPRECATED: Retrieve the dual solution to a linear constraint.
2953
2954        :param Constraint cons: linear constraint
2955
2956        """
2957        raise Warning("model.getDualMultiplier(cons) is deprecated: please use model.getDualsolLinear(cons)")
2958        return self.getDualsolLinear(cons)
2959
2960    def getDualfarkasLinear(self, Constraint cons):
2961        """Retrieve the dual farkas value to a linear constraint.
2962
2963        :param Constraint cons: linear constraint
2964
2965        """
2966        # TODO this should ideally be handled on the SCIP side
2967        if cons.isOriginal():
2968            transcons = <Constraint>self.getTransformedCons(cons)
2969            return SCIPgetDualfarkasLinear(self._scip, transcons.scip_cons)
2970        else:
2971            return SCIPgetDualfarkasLinear(self._scip, cons.scip_cons)
2972
2973    def getVarRedcost(self, Variable var):
2974        """Retrieve the reduced cost of a variable.
2975
2976        :param Variable var: variable to get the reduced cost of
2977
2978        """
2979        redcost = None
2980        try:
2981            redcost = SCIPgetVarRedcost(self._scip, var.scip_var)
2982            if self.getObjectiveSense() == "maximize":
2983                redcost = -redcost
2984        except:
2985            raise Warning("no reduced cost available for variable " + var.name)
2986        return redcost
2987
2988    def optimize(self):
2989        """Optimize the problem."""
2990        PY_SCIP_CALL(SCIPsolve(self._scip))
2991        self._bestSol = Solution.create(self._scip, SCIPgetBestSol(self._scip))
2992
2993    def presolve(self):
2994        """Presolve the problem."""
2995        PY_SCIP_CALL(SCIPpresolve(self._scip))
2996
2997    # Benders' decomposition methods
2998    def initBendersDefault(self, subproblems):
2999        """initialises the default Benders' decomposition with a dictionary of subproblems
3000
3001        Keyword arguments:
3002        subproblems -- a single Model instance or dictionary of Model instances
3003        """
3004        cdef SCIP** subprobs
3005        cdef SCIP_BENDERS* benders
3006
3007        # checking whether subproblems is a dictionary
3008        if isinstance(subproblems, dict):
3009            isdict = True
3010            nsubproblems = len(subproblems)
3011        else:
3012            isdict = False
3013            nsubproblems = 1
3014
3015        # create array of SCIP instances for the subproblems
3016        subprobs = <SCIP**> malloc(nsubproblems * sizeof(SCIP*))
3017
3018        # if subproblems is a dictionary, then the dictionary is turned into a c array
3019        if isdict:
3020            for idx, subprob in enumerate(subproblems.values()):
3021                subprobs[idx] = (<Model>subprob)._scip
3022        else:
3023            subprobs[0] = (<Model>subproblems)._scip
3024
3025        # creating the default Benders' decomposition
3026        PY_SCIP_CALL(SCIPcreateBendersDefault(self._scip, subprobs, nsubproblems))
3027        benders = SCIPfindBenders(self._scip, "default")
3028
3029        # activating the Benders' decomposition constraint handlers
3030        self.setBoolParam("constraints/benderslp/active", True)
3031        self.setBoolParam("constraints/benders/active", True)
3032        #self.setIntParam("limits/maxorigsol", 0)
3033
3034    def computeBestSolSubproblems(self):
3035        """Solves the subproblems with the best solution to the master problem.
3036        Afterwards, the best solution from each subproblem can be queried to get
3037        the solution to the original problem.
3038
3039        If the user wants to resolve the subproblems, they must free them by
3040        calling freeBendersSubproblems()
3041        """
3042        cdef SCIP_BENDERS** _benders
3043        cdef SCIP_Bool _infeasible
3044        cdef int nbenders
3045        cdef int nsubproblems
3046
3047        solvecip = True
3048
3049        nbenders = SCIPgetNActiveBenders(self._scip)
3050        _benders = SCIPgetBenders(self._scip)
3051
3052        # solving all subproblems from all Benders' decompositions
3053        for i in range(nbenders):
3054            nsubproblems = SCIPbendersGetNSubproblems(_benders[i])
3055            for j in range(nsubproblems):
3056                PY_SCIP_CALL(SCIPsetupBendersSubproblem(self._scip,
3057                    _benders[i], self._bestSol.sol, j, SCIP_BENDERSENFOTYPE_CHECK))
3058                PY_SCIP_CALL(SCIPsolveBendersSubproblem(self._scip,
3059                    _benders[i], self._bestSol.sol, j, &_infeasible, solvecip, NULL))
3060
3061    def freeBendersSubproblems(self):
3062        """Calls the free subproblem function for the Benders' decomposition.
3063        This will free all subproblems for all decompositions.
3064        """
3065        cdef SCIP_BENDERS** _benders
3066        cdef int nbenders
3067        cdef int nsubproblems
3068
3069        nbenders = SCIPgetNActiveBenders(self._scip)
3070        _benders = SCIPgetBenders(self._scip)
3071
3072        # solving all subproblems from all Benders' decompositions
3073        for i in range(nbenders):
3074            nsubproblems = SCIPbendersGetNSubproblems(_benders[i])
3075            for j in range(nsubproblems):
3076                PY_SCIP_CALL(SCIPfreeBendersSubproblem(self._scip, _benders[i],
3077                    j))
3078
3079    def updateBendersLowerbounds(self, lowerbounds, Benders benders=None):
3080        """"updates the subproblem lower bounds for benders using
3081        the lowerbounds dict. If benders is None, then the default
3082        Benders' decomposition is updated
3083        """
3084        cdef SCIP_BENDERS* _benders
3085
3086        assert type(lowerbounds) is dict
3087
3088        if benders is None:
3089            _benders = SCIPfindBenders(self._scip, "default")
3090        else:
3091            _benders = benders._benders
3092
3093        for d in lowerbounds.keys():
3094            SCIPbendersUpdateSubproblemLowerbound(_benders, d, lowerbounds[d])
3095
3096    def activateBenders(self, Benders benders, int nsubproblems):
3097        """Activates the Benders' decomposition plugin with the input name
3098
3099        Keyword arguments:
3100        benders -- the Benders' decomposition to which the subproblem belongs to
3101        nsubproblems -- the number of subproblems in the Benders' decomposition
3102        """
3103        PY_SCIP_CALL(SCIPactivateBenders(self._scip, benders._benders, nsubproblems))
3104
3105    def addBendersSubproblem(self, Benders benders, subproblem):
3106        """adds a subproblem to the Benders' decomposition given by the input
3107        name.
3108
3109        Keyword arguments:
3110        benders -- the Benders' decomposition to which the subproblem belongs to
3111        subproblem --  the subproblem to add to the decomposition
3112        isconvex -- can be used to specify whether the subproblem is convex
3113        """
3114        PY_SCIP_CALL(SCIPaddBendersSubproblem(self._scip, benders._benders, (<Model>subproblem)._scip))
3115
3116    def setBendersSubproblemIsConvex(self, Benders benders, probnumber, isconvex = True):
3117        """sets a flag indicating whether the subproblem is convex
3118
3119        Keyword arguments:
3120        benders -- the Benders' decomposition which contains the subproblem
3121        probnumber -- the problem number of the subproblem that the convexity will be set for
3122        isconvex -- flag to indicate whether the subproblem is convex
3123        """
3124        SCIPbendersSetSubproblemIsConvex(benders._benders, probnumber, isconvex)
3125
3126    def setupBendersSubproblem(self, probnumber, Benders benders = None, Solution solution = None, checktype = PY_SCIP_BENDERSENFOTYPE.LP):
3127        """ sets up the Benders' subproblem given the master problem solution
3128
3129        Keyword arguments:
3130        probnumber -- the index of the problem that is to be set up
3131        benders -- the Benders' decomposition to which the subproblem belongs to
3132        solution -- the master problem solution that is used for the set up, if None, then the LP solution is used
3133        checktype -- the type of solution check that prompted the solving of the Benders' subproblems, either
3134            PY_SCIP_BENDERSENFOTYPE: LP, RELAX, PSEUDO or CHECK. Default is LP
3135        """
3136        cdef SCIP_BENDERS* scip_benders
3137        cdef SCIP_SOL* scip_sol
3138
3139        if isinstance(solution, Solution):
3140            scip_sol = solution.sol
3141        else:
3142            scip_sol = NULL
3143
3144        if benders is None:
3145            scip_benders = SCIPfindBenders(self._scip, "default")
3146        else:
3147            scip_benders = benders._benders
3148
3149        retcode = SCIPsetupBendersSubproblem(self._scip, scip_benders, scip_sol, probnumber, checktype)
3150
3151        PY_SCIP_CALL(retcode)
3152
3153    def solveBendersSubproblem(self, probnumber, solvecip, Benders benders = None, Solution solution = None):
3154        """ solves the Benders' decomposition subproblem. The convex relaxation will be solved unless
3155        the parameter solvecip is set to True.
3156
3157        Keyword arguments:
3158        probnumber -- the index of the problem that is to be set up
3159        solvecip -- should the CIP of the subproblem be solved, if False, then only the convex relaxation is solved
3160        benders -- the Benders' decomposition to which the subproblem belongs to
3161        solution -- the master problem solution that is used for the set up, if None, then the LP solution is used
3162        """
3163
3164        cdef SCIP_BENDERS* scip_benders
3165        cdef SCIP_SOL* scip_sol
3166        cdef SCIP_Real objective
3167        cdef SCIP_Bool infeasible
3168
3169        if isinstance(solution, Solution):
3170            scip_sol = solution.sol
3171        else:
3172            scip_sol = NULL
3173
3174        if benders is None:
3175            scip_benders = SCIPfindBenders(self._scip, "default")
3176        else:
3177            scip_benders = benders._benders
3178
3179        PY_SCIP_CALL(SCIPsolveBendersSubproblem(self._scip, scip_benders, scip_sol,
3180            probnumber, &infeasible, solvecip, &objective))
3181
3182        return infeasible, objective
3183
3184    def getBendersSubproblem(self, probnumber, Benders benders = None):
3185        """Returns a Model object that wraps around the SCIP instance of the subproblem.
3186        NOTE: This Model object is just a place holder and SCIP instance will not be freed when the object is destroyed.
3187
3188        Keyword arguments:
3189        probnumber -- the problem number for subproblem that is required
3190        benders -- the Benders' decomposition object for the that the subproblem belongs to (Default = None)
3191        """
3192        cdef SCIP_BENDERS* scip_benders
3193        cdef SCIP* scip_subprob
3194
3195        if benders is None:
3196            scip_benders = SCIPfindBenders(self._scip, "default")
3197        else:
3198            scip_benders = benders._benders
3199
3200        scip_subprob = SCIPbendersSubproblem(scip_benders, probnumber)
3201
3202        return Model.create(scip_subprob)
3203
3204    def getBendersVar(self, Variable var, Benders benders = None, probnumber = -1):
3205        """Returns the variable for the subproblem or master problem
3206        depending on the input probnumber
3207
3208        Keyword arguments:
3209        var -- the source variable for which the target variable is requested
3210        benders -- the Benders' decomposition to which the subproblem variables belong to
3211        probnumber -- the problem number for which the target variable belongs, -1 for master problem
3212        """
3213        cdef SCIP_BENDERS* _benders
3214        cdef SCIP_VAR* _mappedvar
3215
3216        if benders is None:
3217            _benders = SCIPfindBenders(self._scip, "default")
3218        else:
3219            _benders = benders._benders
3220
3221        if probnumber == -1:
3222            PY_SCIP_CALL(SCIPgetBendersMasterVar(self._scip, _benders, var.scip_var, &_mappedvar))
3223        else:
3224            PY_SCIP_CALL(SCIPgetBendersSubproblemVar(self._scip, _benders, var.scip_var, &_mappedvar, probnumber))
3225
3226        if _mappedvar == NULL:
3227            mappedvar = None
3228        else:
3229            mappedvar = Variable.create(_mappedvar)
3230
3231        return mappedvar
3232
3233    def getBendersAuxiliaryVar(self, probnumber, Benders benders = None):
3234        """Returns the auxiliary variable that is associated with the input problem number
3235
3236        Keyword arguments:
3237        probnumber -- the problem number for which the target variable belongs, -1 for master problem
3238        benders -- the Benders' decomposition to which the subproblem variables belong to
3239        """
3240        cdef SCIP_BENDERS* _benders
3241        cdef SCIP_VAR* _auxvar
3242
3243        if benders is None:
3244            _benders = SCIPfindBenders(self._scip, "default")
3245        else:
3246            _benders = benders._benders
3247
3248        _auxvar = SCIPbendersGetAuxiliaryVar(_benders, probnumber)
3249        auxvar = Variable.create(_auxvar)
3250
3251        return auxvar
3252
3253    def checkBendersSubproblemOptimality(self, Solution solution, probnumber, Benders benders = None):
3254        """Returns whether the subproblem is optimal w.r.t the master problem auxiliary variables.
3255
3256        Keyword arguments:
3257        solution -- the master problem solution that is being checked for optimamlity
3258        probnumber -- the problem number for which optimality is being checked
3259        benders -- the Benders' decomposition to which the subproblem belongs to
3260        """
3261        cdef SCIP_BENDERS* _benders
3262        cdef SCIP_SOL* scip_sol
3263        cdef SCIP_Bool optimal
3264
3265        if benders is None:
3266            _benders = SCIPfindBenders(self._scip, "default")
3267        else:
3268            _benders = benders._benders
3269
3270        if isinstance(solution, Solution):
3271            scip_sol = solution.sol
3272        else:
3273            scip_sol = NULL
3274
3275        PY_SCIP_CALL( SCIPcheckBendersSubproblemOptimality(self._scip, _benders,
3276            scip_sol, probnumber, &optimal) )
3277
3278        return optimal
3279
3280    def includeBendersDefaultCuts(self, Benders benders):
3281        """includes the default Benders' decomposition cuts to the custom Benders' decomposition plugin
3282
3283        Keyword arguments:
3284        benders -- the Benders' decomposition that the default cuts will be applied to
3285        """
3286        PY_SCIP_CALL( SCIPincludeBendersDefaultCuts(self._scip, benders._benders) )
3287
3288
3289    def includeEventhdlr(self, Eventhdlr eventhdlr, name, desc):
3290        """Include an event handler.
3291
3292        Keyword arguments:
3293        eventhdlr -- event handler
3294        name -- name of event handler
3295        desc -- description of event handler
3296        """
3297        n = str_conversion(name)
3298        d = str_conversion(desc)
3299        PY_SCIP_CALL(SCIPincludeEventhdlr(self._scip, n, d,
3300                                          PyEventCopy,
3301                                          PyEventFree,
3302                                          PyEventInit,
3303                                          PyEventExit,
3304                                          PyEventInitsol,
3305                                          PyEventExitsol,
3306                                          PyEventDelete,
3307                                          PyEventExec,
3308                                          <SCIP_EVENTHDLRDATA*>eventhdlr))
3309        eventhdlr.model = <Model>weakref.proxy(self)
3310        eventhdlr.name = name
3311        Py_INCREF(eventhdlr)
3312
3313    def includePricer(self, Pricer pricer, name, desc, priority=1, delay=True):
3314        """Include a pricer.
3315
3316        :param Pricer pricer: pricer
3317        :param name: name of pricer
3318        :param desc: description of pricer
3319        :param priority: priority of pricer (Default value = 1)
3320        :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)
3321
3322        """
3323        n = str_conversion(name)
3324        d = str_conversion(desc)
3325        PY_SCIP_CALL(SCIPincludePricer(self._scip, n, d,
3326                                            priority, delay,
3327                                            PyPricerCopy, PyPricerFree, PyPricerInit, PyPricerExit, PyPricerInitsol, PyPricerExitsol, PyPricerRedcost, PyPricerFarkas,
3328                                            <SCIP_PRICERDATA*>pricer))
3329        cdef SCIP_PRICER* scip_pricer
3330        scip_pricer = SCIPfindPricer(self._scip, n)
3331        PY_SCIP_CALL(SCIPactivatePricer(self._scip, scip_pricer))
3332        pricer.model = <Model>weakref.proxy(self)
3333        Py_INCREF(pricer)
3334
3335    def includeConshdlr(self, Conshdlr conshdlr, name, desc, sepapriority=0,
3336                        enfopriority=0, chckpriority=0, sepafreq=-1, propfreq=-1,
3337                        eagerfreq=100, maxprerounds=-1, delaysepa=False,
3338                        delayprop=False, needscons=True,
3339                        proptiming=PY_SCIP_PROPTIMING.BEFORELP,
3340                        presoltiming=PY_SCIP_PRESOLTIMING.MEDIUM):
3341        """Include a constraint handler
3342
3343        :param Conshdlr conshdlr: constraint handler
3344        :param name: name of constraint handler
3345        :param desc: description of constraint handler
3346        :param sepapriority: priority for separation (Default value = 0)
3347        :param enfopriority: priority for constraint enforcing (Default value = 0)
3348        :param chckpriority: priority for checking feasibility (Default value = 0)
3349        :param sepafreq: frequency for separating cuts; 0 = only at root node (Default value = -1)
3350        :param propfreq: frequency for propagating domains; 0 = only preprocessing propagation (Default value = -1)
3351        :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)
3352        :param maxprerounds: maximal number of presolving rounds the constraint handler participates in (Default value = -1)
3353        :param delaysepa: should separation method be delayed, if other separators found cuts? (Default value = False)
3354        :param delayprop: should propagation method be delayed, if other propagators found reductions? (Default value = False)
3355        :param needscons: should the constraint handler be skipped, if no constraints are available? (Default value = True)
3356        :param proptiming: positions in the node solving loop where propagation method of constraint handlers should be executed (Default value = SCIP_PROPTIMING.BEFORELP)
3357        :param presoltiming: timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING.MEDIUM)
3358
3359        """
3360        n = str_conversion(name)
3361        d = str_conversion(desc)
3362        PY_SCIP_CALL(SCIPincludeConshdlr(self._scip, n, d, sepapriority, enfopriority, chckpriority, sepafreq, propfreq, eagerfreq,
3363                                              maxprerounds, delaysepa, delayprop, needscons, proptiming, presoltiming,
3364                                              PyConshdlrCopy, PyConsFree, PyConsInit, PyConsExit, PyConsInitpre, PyConsExitpre,
3365                                              PyConsInitsol, PyConsExitsol, PyConsDelete, PyConsTrans, PyConsInitlp, PyConsSepalp, PyConsSepasol,
3366                                              PyConsEnfolp, PyConsEnforelax, PyConsEnfops, PyConsCheck, PyConsProp, PyConsPresol, PyConsResprop, PyConsLock,
3367                                              PyConsActive, PyConsDeactive, PyConsEnable, PyConsDisable, PyConsDelvars, PyConsPrint, PyConsCopy,
3368                                              PyConsParse, PyConsGetvars, PyConsGetnvars, PyConsGetdivebdchgs,
3369                                              <SCIP_CONSHDLRDATA*>conshdlr))
3370        conshdlr.model = <Model>weakref.proxy(self)
3371        conshdlr.name = name
3372        Py_INCREF(conshdlr)
3373
3374    def createCons(self, Conshdlr conshdlr, name, initial=True, separate=True, enforce=True, check=True, propagate=True,
3375                   local=False, modifiable=False, dynamic=False, removable=False, stickingatnode=False):
3376        """Create a constraint of a custom constraint handler
3377
3378        :param Conshdlr conshdlr: constraint handler
3379        :param name: name of constraint
3380        :param initial:  (Default value = True)
3381        :param separate:  (Default value = True)
3382        :param enforce:  (Default value = True)
3383        :param check:  (Default value = True)
3384        :param propagate:  (Default value = True)
3385        :param local:  (Default value = False)
3386        :param modifiable:  (Default value = False)
3387        :param dynamic:  (Default value = False)
3388        :param removable:  (Default value = False)
3389        :param stickingatnode:  (Default value = False)
3390
3391        """
3392
3393        n = str_conversion(name)
3394        cdef SCIP_CONSHDLR* scip_conshdlr
3395        scip_conshdlr = SCIPfindConshdlr(self._scip, str_conversion(conshdlr.name))
3396        constraint = Constraint()
3397        PY_SCIP_CALL(SCIPcreateCons(self._scip, &(constraint.scip_cons), n, scip_conshdlr, <SCIP_CONSDATA*>constraint,
3398                                initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
3399        return constraint
3400
3401    def includePresol(self, Presol presol, name, desc, priority, maxrounds, timing=SCIP_PRESOLTIMING_FAST):
3402        """Include a presolver
3403
3404        :param Presol presol: presolver
3405        :param name: name of presolver
3406        :param desc: description of presolver
3407        :param priority: priority of the presolver (>= 0: before, < 0: after constraint handlers)
3408        :param maxrounds: maximal number of presolving rounds the presolver participates in (-1: no limit)
3409        :param timing: timing mask of presolver (Default value = SCIP_PRESOLTIMING_FAST)
3410
3411        """
3412        n = str_conversion(name)
3413        d = str_conversion(desc)
3414        PY_SCIP_CALL(SCIPincludePresol(self._scip, n, d, priority, maxrounds, timing, PyPresolCopy, PyPresolFree, PyPresolInit,
3415                                            PyPresolExit, PyPresolInitpre, PyPresolExitpre, PyPresolExec, <SCIP_PRESOLDATA*>presol))
3416        presol.model = <Model>weakref.proxy(self)
3417        Py_INCREF(presol)
3418
3419    def includeSepa(self, Sepa sepa, name, desc, priority=0, freq=10, maxbounddist=1.0, usessubscip=False, delay=False):
3420        """Include a separator
3421
3422        :param Sepa sepa: separator
3423        :param name: name of separator
3424        :param desc: description of separator
3425        :param priority: priority of separator (>= 0: before, < 0: after constraint handlers)
3426        :param freq: frequency for calling separator
3427        :param maxbounddist: maximal relative distance from current node's dual bound to primal bound compared to best node's dual bound for applying separation
3428        :param usessubscip: does the separator use a secondary SCIP instance? (Default value = False)
3429        :param delay: should separator be delayed, if other separators found cuts? (Default value = False)
3430
3431        """
3432        n = str_conversion(name)
3433        d = str_conversion(desc)
3434        PY_SCIP_CALL(SCIPincludeSepa(self._scip, n, d, priority, freq, maxbounddist, usessubscip, delay, PySepaCopy, PySepaFree,
3435                                          PySepaInit, PySepaExit, PySepaInitsol, PySepaExitsol, PySepaExeclp, PySepaExecsol, <SCIP_SEPADATA*>sepa))
3436        sepa.model = <Model>weakref.proxy(self)
3437        sepa.name = name
3438        Py_INCREF(sepa)
3439
3440    def includeProp(self, Prop prop, name, desc, presolpriority, presolmaxrounds,
3441                    proptiming, presoltiming=SCIP_PRESOLTIMING_FAST, priority=1, freq=1, delay=True):
3442        """Include a propagator.
3443
3444        :param Prop prop: propagator
3445        :param name: name of propagator
3446        :param desc: description of propagator
3447        :param presolpriority: presolving priority of the propgator (>= 0: before, < 0: after constraint handlers)
3448        :param presolmaxrounds: maximal number of presolving rounds the propagator participates in (-1: no limit)
3449        :param proptiming: positions in the node solving loop where propagation method of constraint handlers should be executed
3450        :param presoltiming: timing mask of the constraint handler's presolving method (Default value = SCIP_PRESOLTIMING_FAST)
3451        :param priority: priority of the propagator (Default value = 1)
3452        :param freq: frequency for calling propagator (Default value = 1)
3453        :param delay: should propagator be delayed if other propagators have found reductions? (Default value = True)
3454
3455        """
3456        n = str_conversion(name)
3457        d = str_conversion(desc)
3458        PY_SCIP_CALL(SCIPincludeProp(self._scip, n, d,
3459                                          priority, freq, delay,
3460                                          proptiming, presolpriority, presolmaxrounds,
3461                                          presoltiming, PyPropCopy, PyPropFree, PyPropInit, PyPropExit,
3462                                          PyPropInitpre, PyPropExitpre, PyPropInitsol, PyPropExitsol,
3463                                          PyPropPresol, PyPropExec, PyPropResProp,
3464                                          <SCIP_PROPDATA*> prop))
3465        prop.model = <Model>weakref.proxy(self)
3466        Py_INCREF(prop)
3467
3468    def includeHeur(self, Heur heur, name, desc, dispchar, priority=10000, freq=1, freqofs=0,
3469                    maxdepth=-1, timingmask=SCIP_HEURTIMING_BEFORENODE, usessubscip=False):
3470        """Include a primal heuristic.
3471
3472        :param Heur heur: heuristic
3473        :param name: name of heuristic
3474        :param desc: description of heuristic
3475        :param dispchar: display character of heuristic
3476        :param priority: priority of the heuristic (Default value = 10000)
3477        :param freq: frequency for calling heuristic (Default value = 1)
3478        :param freqofs: frequency offset for calling heuristic (Default value = 0)
3479        :param maxdepth: maximal depth level to call heuristic at (Default value = -1)
3480        :param timingmask: positions in the node solving loop where heuristic should be executed (Default value = SCIP_HEURTIMING_BEFORENODE)
3481        :param usessubscip: does the heuristic use a secondary SCIP instance? (Default value = False)
3482
3483        """
3484        nam = str_conversion(name)
3485        des = str_conversion(desc)
3486        dis = ord(str_conversion(dispchar))
3487        PY_SCIP_CALL(SCIPincludeHeur(self._scip, nam, des, dis,
3488                                          priority, freq, freqofs,
3489                                          maxdepth, timingmask, usessubscip,
3490                                          PyHeurCopy, PyHeurFree, PyHeurInit, PyHeurExit,
3491                                          PyHeurInitsol, PyHeurExitsol, PyHeurExec,
3492                                          <SCIP_HEURDATA*> heur))
3493        heur.model = <Model>weakref.proxy(self)
3494        heur.name = name
3495        Py_INCREF(heur)
3496
3497    def includeRelax(self, Relax relax, name, desc, priority=10000, freq=1):
3498        """Include a relaxation handler.
3499
3500        :param Relax relax: relaxation handler
3501        :param name: name of relaxation handler
3502        :param desc: description of relaxation handler
3503        :param priority: priority of the relaxation handler (negative: after LP, non-negative: before LP, Default value = 10000)
3504        :param freq: frequency for calling relaxation handler
3505
3506        """
3507        nam = str_conversion(name)
3508        des = str_conversion(desc)
3509        PY_SCIP_CALL(SCIPincludeRelax(self._scip, nam, des, priority, freq, PyRelaxCopy, PyRelaxFree, PyRelaxInit, PyRelaxExit,
3510                                          PyRelaxInitsol, PyRelaxExitsol, PyRelaxExec, <SCIP_RELAXDATA*> relax))
3511        relax.model = <Model>weakref.proxy(self)
3512        relax.name = name
3513
3514        Py_INCREF(relax)
3515
3516    def includeBranchrule(self, Branchrule branchrule, name, desc, priority, maxdepth, maxbounddist):
3517        """Include a branching rule.
3518
3519        :param Branchrule branchrule: branching rule
3520        :param name: name of branching rule
3521        :param desc: description of branching rule
3522        :param priority: priority of branching rule
3523        :param maxdepth: maximal depth level up to which this branching rule should be used (or -1)
3524        :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)
3525
3526        """
3527        nam = str_conversion(name)
3528        des = str_conversion(desc)
3529        PY_SCIP_CALL(SCIPincludeBranchrule(self._scip, nam, des,
3530                                          priority, maxdepth, maxbounddist,
3531                                          PyBranchruleCopy, PyBranchruleFree, PyBranchruleInit, PyBranchruleExit,
3532                                          PyBranchruleInitsol, PyBranchruleExitsol, PyBranchruleExeclp, PyBranchruleExecext,
3533                                          PyBranchruleExecps, <SCIP_BRANCHRULEDATA*> branchrule))
3534        branchrule.model = <Model>weakref.proxy(self)
3535        Py_INCREF(branchrule)
3536
3537    def includeNodesel(self, Nodesel nodesel, name, desc, stdpriority, memsavepriority):
3538        """Include a node selector.
3539
3540        :param Nodesel nodesel: node selector
3541        :param name: name of node selector
3542        :param desc: description of node selector
3543        :param stdpriority: priority of the node selector in standard mode
3544        :param memsavepriority: priority of the node selector in memory saving mode
3545
3546        """
3547        nam = str_conversion(name)
3548        des = str_conversion(desc)
3549        PY_SCIP_CALL(SCIPincludeNodesel(self._scip, nam, des,
3550                                          stdpriority, memsavepriority,
3551                                          PyNodeselCopy, PyNodeselFree, PyNodeselInit, PyNodeselExit,
3552                                          PyNodeselInitsol, PyNodeselExitsol, PyNodeselSelect, PyNodeselComp,
3553                                          <SCIP_NODESELDATA*> nodesel))
3554        nodesel.model = <Model>weakref.proxy(self)
3555        Py_INCREF(nodesel)
3556
3557    def includeBenders(self, Benders benders, name, desc, priority=1, cutlp=True, cutpseudo=True, cutrelax=True,
3558            shareaux=False):
3559        """Include a Benders' decomposition.
3560
3561        Keyword arguments:
3562        benders -- the Benders decomposition
3563        name -- the name
3564        desc -- the description
3565        priority -- priority of the Benders' decomposition
3566        cutlp -- should Benders' cuts be generated from LP solutions
3567        cutpseudo -- should Benders' cuts be generated from pseudo solutions
3568        cutrelax -- should Benders' cuts be generated from relaxation solutions
3569        shareaux -- should the Benders' decomposition share the auxiliary variables of the highest priority Benders' decomposition
3570        """
3571        n = str_conversion(name)
3572        d = str_conversion(desc)
3573        PY_SCIP_CALL(SCIPincludeBenders(self._scip, n, d,
3574                                            priority, cutlp, cutrelax, cutpseudo, shareaux,
3575                                            PyBendersCopy, PyBendersFree, PyBendersInit, PyBendersExit, PyBendersInitpre,
3576                                            PyBendersExitpre, PyBendersInitsol, PyBendersExitsol, PyBendersGetvar,
3577                                            PyBendersCreatesub, PyBendersPresubsolve, PyBendersSolvesubconvex,
3578                                            PyBendersSolvesub, PyBendersPostsolve, PyBendersFreesub,
3579                                            <SCIP_BENDERSDATA*>benders))
3580        cdef SCIP_BENDERS* scip_benders
3581        scip_benders = SCIPfindBenders(self._scip, n)
3582        benders.model = <Model>weakref.proxy(self)
3583        benders.name = name
3584        benders._benders = scip_benders
3585        Py_INCREF(benders)
3586
3587    def includeBenderscut(self, Benders benders, Benderscut benderscut, name, desc, priority=1, islpcut=True):
3588        """ Include a Benders' decomposition cutting method
3589
3590        Keyword arguments:
3591        benders -- the Benders' decomposition that this cutting method is attached to
3592        benderscut --- the Benders' decomposition cutting method
3593        name -- the name
3594        desc -- the description
3595        priority -- priority of the Benders' decomposition
3596        islpcut -- is this cutting method suitable for generating cuts for convex relaxations?
3597        """
3598        cdef SCIP_BENDERS* _benders
3599
3600        _benders = benders._benders
3601
3602        n = str_conversion(name)
3603        d = str_conversion(desc)
3604        PY_SCIP_CALL(SCIPincludeBenderscut(self._scip, _benders, n, d, priority, islpcut,
3605                                            PyBenderscutCopy, PyBenderscutFree, PyBenderscutInit, PyBenderscutExit,
3606                                            PyBenderscutInitsol, PyBenderscutExitsol, PyBenderscutExec,
3607                                            <SCIP_BENDERSCUTDATA*>benderscut))
3608
3609        cdef SCIP_BENDERSCUT* scip_benderscut
3610        scip_benderscut = SCIPfindBenderscut(_benders, n)
3611        benderscut.model = <Model>weakref.proxy(self)
3612        benderscut.benders = benders
3613        benderscut.name = name
3614        # TODO: It might be necessary in increment the reference to benders i.e Py_INCREF(benders)
3615        Py_INCREF(benderscut)
3616
3617
3618    def getLPBranchCands(self):
3619        """gets branching candidates for LP solution branching (fractional variables) along with solution values,
3620        fractionalities, and number of branching candidates; The number of branching candidates does NOT account
3621        for fractional implicit integer variables which should not be used for branching decisions. Fractional
3622        implicit integer variables are stored at the positions *nlpcands to *nlpcands + *nfracimplvars - 1
3623        branching rules should always select the branching candidate among the first npriolpcands of the candidate list
3624
3625        :return tuple (lpcands, lpcandssol, lpcadsfrac, nlpcands, npriolpcands, nfracimplvars) where
3626
3627            lpcands: list of variables of LP branching candidates
3628            lpcandssol: list of LP candidate solution values
3629            lpcandsfrac	list of LP candidate fractionalities
3630            nlpcands:    number of LP branching candidates
3631            npriolpcands: number of candidates with maximal priority
3632            nfracimplvars: number of fractional implicit integer variables
3633
3634        """
3635        cdef int ncands
3636        cdef int nlpcands
3637        cdef int npriolpcands
3638        cdef int nfracimplvars
3639
3640        cdef SCIP_VAR** lpcands
3641        cdef SCIP_Real* lpcandssol
3642        cdef SCIP_Real* lpcandsfrac
3643
3644        PY_SCIP_CALL(SCIPgetLPBranchCands(self._scip, &lpcands, &lpcandssol, &lpcandsfrac,
3645                                          &nlpcands, &npriolpcands, &nfracimplvars))
3646
3647        return ([Variable.create(lpcands[i]) for i in range(nlpcands)], [lpcandssol[i] for i in range(nlpcands)],
3648                [lpcandsfrac[i] for i in range(nlpcands)], nlpcands, npriolpcands, nfracimplvars)
3649
3650
3651    def branchVar(self, variable):
3652        """Branch on a non-continuous variable.
3653
3654        :param variable: Variable to branch on
3655        :return: tuple(downchild, eqchild, upchild) of Nodes of the left, middle and right child. Middle child only exists
3656                    if branch variable is integer (it is None otherwise)
3657
3658        """
3659        cdef SCIP_NODE* downchild
3660        cdef SCIP_NODE* eqchild
3661        cdef SCIP_NODE* upchild
3662
3663        PY_SCIP_CALL(SCIPbranchVar(self._scip, (<Variable>variable).scip_var, &downchild, &eqchild, &upchild))
3664        return Node.create(downchild), Node.create(eqchild), Node.create(upchild)
3665
3666
3667    def branchVarVal(self, variable, value):
3668        """Branches on variable using a value which separates the domain of the variable.
3669
3670        :param variable: Variable to branch on
3671        :param value: float, value to branch on
3672        :return: tuple(downchild, eqchild, upchild) of Nodes of the left, middle and right child. Middle child only exists
3673                    if branch variable is integer (it is None otherwise)
3674
3675        """
3676        cdef SCIP_NODE* downchild
3677        cdef SCIP_NODE* eqchild
3678        cdef SCIP_NODE* upchild
3679
3680        PY_SCIP_CALL(SCIPbranchVarVal(self._scip, (<Variable>variable).scip_var, value, &downchild, &eqchild, &upchild))
3681
3682        return Node.create(downchild), Node.create(eqchild), Node.create(upchild)
3683
3684    def calcNodeselPriority(self, Variable variable, branchdir, targetvalue):
3685        """calculates the node selection priority for moving the given variable's LP value
3686        to the given target value;
3687        this node selection priority can be given to the SCIPcreateChild() call
3688
3689        :param variable: variable on which the branching is applied
3690        :param branchdir: type of branching that was performed
3691        :param targetvalue: new value of the variable in the child node
3692        :return: node selection priority for moving the given variable's LP value to the given target value
3693
3694        """
3695        return SCIPcalcNodeselPriority(self._scip, variable.scip_var, branchdir, targetvalue)
3696
3697    def calcChildEstimate(self, Variable variable, targetvalue):
3698        """Calculates an estimate for the objective of the best feasible solution
3699        contained in the subtree after applying the given branching;
3700        this estimate can be given to the SCIPcreateChild() call
3701
3702        :param variable: Variable to compute the estimate for
3703        :param targetvalue: new value of the variable in the child node
3704        :return: objective estimate of the best solution in the subtree after applying the given branching
3705
3706        """
3707        return SCIPcalcChildEstimate(self._scip, variable.scip_var, targetvalue)
3708
3709    def createChild(self, nodeselprio, estimate):
3710        """Create a child node of the focus node.
3711
3712        :param nodeselprio: float, node selection priority of new node
3713        :param estimate: float, estimate for(transformed) objective value of best feasible solution in subtree
3714        :return: Node, the child which was created
3715
3716        """
3717        cdef SCIP_NODE* child
3718        PY_SCIP_CALL(SCIPcreateChild(self._scip, &child, nodeselprio, estimate))
3719        return Node.create(child)
3720
3721    # Diving methods (Diving is LP related)
3722    def startDive(self):
3723        """Initiates LP diving
3724        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,
3725        SCIP will undo all changes done and recover the LP it had before startDive
3726        """
3727        PY_SCIP_CALL(SCIPstartDive(self._scip))
3728
3729    def endDive(self):
3730        """Quits probing and resets bounds and constraints to the focus node's environment"""
3731        PY_SCIP_CALL(SCIPendDive(self._scip))
3732
3733    def chgVarObjDive(self, Variable var, newobj):
3734        """changes (column) variable's objective value in current dive"""
3735        PY_SCIP_CALL(SCIPchgVarObjDive(self._scip, var.scip_var, newobj))
3736
3737    def chgVarLbDive(self, Variable var, newbound):
3738        """changes variable's current lb in current dive"""
3739        PY_SCIP_CALL(SCIPchgVarLbDive(self._scip, var.scip_var, newbound))
3740
3741    def chgVarUbDive(self, Variable var, newbound):
3742        """changes variable's current ub in current dive"""
3743        PY_SCIP_CALL(SCIPchgVarUbDive(self._scip, var.scip_var, newbound))
3744
3745    def getVarLbDive(self, Variable var):
3746        """returns variable's current lb in current dive"""
3747        return SCIPgetVarLbDive(self._scip, var.scip_var)
3748
3749    def getVarUbDive(self, Variable var):
3750        """returns variable's current ub in current dive"""
3751        return SCIPgetVarUbDive(self._scip, var.scip_var)
3752
3753    def chgRowLhsDive(self, Row row, newlhs):
3754        """changes row lhs in current dive, change will be undone after diving
3755        ends, for permanent changes use SCIPchgRowLhs()
3756        """
3757        PY_SCIP_CALL(SCIPchgRowLhsDive(self._scip, row.scip_row, newlhs))
3758
3759    def chgRowRhsDive(self, Row row, newrhs):
3760        """changes row rhs in current dive, change will be undone after diving
3761        ends, for permanent changes use SCIPchgRowLhs()
3762        """
3763        PY_SCIP_CALL(SCIPchgRowRhsDive(self._scip, row.scip_row, newrhs))
3764
3765    def addRowDive(self, Row row):
3766        """adds a row to the LP in current dive"""
3767        PY_SCIP_CALL(SCIPaddRowDive(self._scip, row.scip_row))
3768
3769    def solveDiveLP(self, itlim = -1):
3770        """solves the LP of the current dive no separation or pricing is applied
3771        no separation or pricing is applied
3772        :param itlim: maximal number of LP iterations to perform (Default value = -1, that is, no limit)
3773        returns two booleans:
3774        lperror -- if an unresolved lp error occured
3775        cutoff -- whether the LP was infeasible or the objective limit was reached
3776        """
3777        cdef SCIP_Bool lperror
3778        cdef SCIP_Bool cutoff
3779
3780        PY_SCIP_CALL(SCIPsolveDiveLP(self._scip, itlim, &lperror, &cutoff))
3781        return lperror, cutoff
3782
3783    def inRepropagation(self):
3784        """returns if the current node is already solved and only propagated again."""
3785        return SCIPinRepropagation(self._scip)
3786
3787    # Probing methods (Probing is tree based)
3788    def startProbing(self):
3789        """Initiates probing, making methods SCIPnewProbingNode(), SCIPbacktrackProbing(), SCIPchgVarLbProbing(),
3790           SCIPchgVarUbProbing(), SCIPfixVarProbing(), SCIPpropagateProbing(), SCIPsolveProbingLP(), etc available
3791        """
3792        PY_SCIP_CALL( SCIPstartProbing(self._scip) )
3793
3794    def endProbing(self):
3795        """Quits probing and resets bounds and constraints to the focus node's environment"""
3796        PY_SCIP_CALL( SCIPendProbing(self._scip) )
3797
3798    def newProbingNode(self):
3799        """creates a new probing sub node, whose changes can be undone by backtracking to a higher node in the
3800        probing path with a call to backtrackProbing()
3801        """
3802        PY_SCIP_CALL( SCIPnewProbingNode(self._scip) )
3803
3804    def backtrackProbing(self, probingdepth):
3805        """undoes all changes to the problem applied in probing up to the given probing depth
3806        :param probingdepth: probing depth of the node in the probing path that should be reactivated
3807        """
3808        PY_SCIP_CALL( SCIPbacktrackProbing(self._scip, probingdepth) )
3809
3810    def getProbingDepth(self):
3811        """returns the current probing depth"""
3812        return SCIPgetProbingDepth(self._scip)
3813
3814    def chgVarObjProbing(self, Variable var, newobj):
3815        """changes (column) variable's objective value during probing mode"""
3816        PY_SCIP_CALL( SCIPchgVarObjProbing(self._scip, var.scip_var, newobj) )
3817
3818    def chgVarLbProbing(self, Variable var, lb):
3819        """changes the variable lower bound during probing mode
3820
3821        :param Variable var: variable to change bound of
3822        :param lb: new lower bound (set to None for -infinity)
3823        """
3824        if lb is None:
3825           lb = -SCIPinfinity(self._scip)
3826        PY_SCIP_CALL(SCIPchgVarLbProbing(self._scip, var.scip_var, lb))
3827
3828    def chgVarUbProbing(self, Variable var, ub):
3829        """changes the variable upper bound during probing mode
3830
3831        :param Variable var: variable to change bound of
3832        :param ub: new upper bound (set to None for +infinity)
3833        """
3834        if ub is None:
3835           ub = SCIPinfinity(self._scip)
3836        PY_SCIP_CALL(SCIPchgVarUbProbing(self._scip, var.scip_var, ub))
3837
3838    def fixVarProbing(self, Variable var, fixedval):
3839        """Fixes a variable at the current probing node."""
3840        PY_SCIP_CALL( SCIPfixVarProbing(self._scip, var.scip_var, fixedval) )
3841
3842    def isObjChangedProbing(self):
3843        """returns whether the objective function has changed during probing mode"""
3844        return SCIPisObjChangedProbing(self._scip)
3845
3846    def inProbing(self):
3847        """returns whether we are in probing mode; probing mode is activated via startProbing() and stopped via endProbing()"""
3848        return SCIPinProbing(self._scip)
3849
3850    def solveProbingLP(self, itlim = -1):
3851        """solves the LP at the current probing node (cannot be applied at preprocessing stage)
3852        no separation or pricing is applied
3853        :param itlim: maximal number of LP iterations to perform (Default value = -1, that is, no limit)
3854        returns two booleans:
3855        lperror -- if an unresolved lp error occured
3856        cutoff -- whether the LP was infeasible or the objective limit was reached
3857        """
3858        cdef SCIP_Bool lperror
3859        cdef SCIP_Bool cutoff
3860
3861        PY_SCIP_CALL( SCIPsolveProbingLP(self._scip, itlim, &lperror, &cutoff) )
3862        return lperror, cutoff
3863
3864    def applyCutsProbing(self):
3865        """applies the cuts in the separation storage to the LP and clears the storage afterwards;
3866        this method can only be applied during probing; the user should resolve the probing LP afterwards
3867        in order to get a new solution
3868        returns:
3869        cutoff -- whether an empty domain was created
3870        """
3871        cdef SCIP_Bool cutoff
3872
3873        PY_SCIP_CALL( SCIPapplyCutsProbing(self._scip, &cutoff) )
3874        return cutoff
3875
3876    def propagateProbing(self, maxproprounds):
3877        """applies domain propagation on the probing sub problem, that was changed after SCIPstartProbing() was called;
3878        the propagated domains of the variables can be accessed with the usual bound accessing calls SCIPvarGetLbLocal()
3879        and SCIPvarGetUbLocal(); the propagation is only valid locally, i.e. the local bounds as well as the changed
3880        bounds due to SCIPchgVarLbProbing(), SCIPchgVarUbProbing(), and SCIPfixVarProbing() are used for propagation
3881        :param maxproprounds: maximal number of propagation rounds (Default value = -1, that is, no limit)
3882        returns:
3883        cutoff -- whether the probing node can be cutoff
3884        ndomredsfound -- number of domain reductions found
3885        """
3886        cdef SCIP_Bool cutoff
3887        cdef SCIP_Longint ndomredsfound
3888
3889        PY_SCIP_CALL( SCIPpropagateProbing(self._scip, maxproprounds, &cutoff, &ndomredsfound) )
3890        return cutoff, ndomredsfound
3891
3892    def interruptSolve(self):
3893        """Interrupt the solving process as soon as possible."""
3894        PY_SCIP_CALL(SCIPinterruptSolve(self._scip))
3895
3896    def restartSolve(self):
3897        """Restarts the solving process as soon as possible."""
3898        PY_SCIP_CALL(SCIPrestartSolve(self._scip))
3899
3900    # Solution functions
3901
3902    def writeLP(self, filename="LP.lp"):
3903        """writes current LP to a file
3904        :param filename: file name (Default value = "LP.lp")
3905        """
3906        PY_SCIP_CALL( SCIPwriteLP(self._scip, str_conversion(filename)) )
3907
3908    def createSol(self, Heur heur = None):
3909        """Create a new primal solution.
3910
3911        :param Heur heur: heuristic that found the solution (Default value = None)
3912
3913        """
3914        cdef SCIP_HEUR* _heur
3915        cdef SCIP_SOL* _sol
3916
3917        if isinstance(heur, Heur):
3918            n = str_conversion(heur.name)
3919            _heur = SCIPfindHeur(self._scip, n)
3920        else:
3921            _heur = NULL
3922        PY_SCIP_CALL(SCIPcreateSol(self._scip, &_sol, _heur))
3923        solution = Solution.create(self._scip, _sol)
3924        return solution
3925
3926    def printBestSol(self, write_zeros=False):
3927        """Prints the best feasible primal solution."""
3928        PY_SCIP_CALL(SCIPprintBestSol(self._scip, NULL, write_zeros))
3929
3930    def printSol(self, Solution solution=None, write_zeros=False):
3931      """Print the given primal solution.
3932
3933      Keyword arguments:
3934      solution -- solution to print
3935      write_zeros -- include variables that are set to zero
3936      """
3937      if solution is None:
3938         PY_SCIP_CALL(SCIPprintSol(self._scip, NULL, NULL, write_zeros))
3939      else:
3940         PY_SCIP_CALL(SCIPprintSol(self._scip, solution.sol, NULL, write_zeros))
3941
3942    def writeBestSol(self, filename="origprob.sol", write_zeros=False):
3943        """Write the best feasible primal solution to a file.
3944
3945        Keyword arguments:
3946        filename -- name of the output file
3947        write_zeros -- include variables that are set to zero
3948        """
3949        # use this doubled opening pattern to ensure that IOErrors are
3950        #   triggered early and in Python not in C,Cython or SCIP.
3951        with open(filename, "w") as f:
3952            cfile = fdopen(f.fileno(), "w")
3953            PY_SCIP_CALL(SCIPprintBestSol(self._scip, cfile, write_zeros))
3954
3955    def writeSol(self, Solution solution, filename="origprob.sol", write_zeros=False):
3956        """Write the given primal solution to a file.
3957
3958        Keyword arguments:
3959        solution -- solution to write
3960        filename -- name of the output file
3961        write_zeros -- include variables that are set to zero
3962        """
3963        # use this doubled opening pattern to ensure that IOErrors are
3964        #   triggered early and in Python not in C,Cython or SCIP.
3965        with open(filename, "w") as f:
3966            cfile = fdopen(f.fileno(), "w")
3967            PY_SCIP_CALL(SCIPprintSol(self._scip, solution.sol, cfile, write_zeros))
3968
3969    # perhaps this should not be included as it implements duplicated functionality
3970    #   (as does it's namesake in SCIP)
3971    def readSol(self, filename):
3972        """Reads a given solution file, problem has to be transformed in advance.
3973
3974        Keyword arguments:
3975        filename -- name of the input file
3976        """
3977        fn = str_conversion(filename)
3978        PY_SCIP_CALL(SCIPreadSol(self._scip, fn))
3979
3980    def readSolFile(self, filename):
3981        """Reads a given solution file.
3982
3983        Solution is created but not added to storage/the model.
3984        Use 'addSol' OR 'trySol' to add it.
3985
3986        Keyword arguments:
3987        filename -- name of the input file
3988        """
3989        cdef SCIP_Bool partial
3990        cdef SCIP_Bool error
3991        cdef SCIP_Bool stored
3992        cdef Solution solution
3993
3994        fn = str_conversion(filename)
3995        solution = self.createSol()
3996        PY_SCIP_CALL(SCIPreadSolFile(self._scip, fn, solution.sol, False, &partial, &error))
3997        if error:
3998            raise Exception("SCIP: reading solution from file failed!")
3999
4000        return solution
4001
4002    def setSolVal(self, Solution solution, Variable var, val):
4003        """Set a variable in a solution.
4004
4005        :param Solution solution: solution to be modified
4006        :param Variable var: variable in the solution
4007        :param val: value of the specified variable
4008
4009        """
4010        cdef SCIP_SOL* _sol
4011        _sol = <SCIP_SOL*>solution.sol
4012        PY_SCIP_CALL(SCIPsetSolVal(self._scip, _sol, var.scip_var, val))
4013
4014    def trySol(self, Solution solution, printreason=True, completely=False, checkbounds=True, checkintegrality=True, checklprows=True, free=True):
4015        """Check given primal solution for feasibility and try to add it to the storage.
4016
4017        :param Solution solution: solution to store
4018        :param printreason: should all reasons of violations be printed? (Default value = True)
4019        :param completely: should all violation be checked? (Default value = False)
4020        :param checkbounds: should the bounds of the variables be checked? (Default value = True)
4021        :param checkintegrality: has integrality to be checked? (Default value = True)
4022        :param checklprows: have current LP rows (both local and global) to be checked? (Default value = True)
4023        :param free: should solution be freed? (Default value = True)
4024
4025        """
4026        cdef SCIP_Bool stored
4027        if free:
4028            PY_SCIP_CALL(SCIPtrySolFree(self._scip, &solution.sol, printreason, completely, checkbounds, checkintegrality, checklprows, &stored))
4029        else:
4030            PY_SCIP_CALL(SCIPtrySol(self._scip, solution.sol, printreason, completely, checkbounds, checkintegrality, checklprows, &stored))
4031        return stored
4032
4033    def checkSol(self, Solution solution, printreason=True, completely=False, checkbounds=True, checkintegrality=True, checklprows=True, original=False):
4034        """Check given primal solution for feasibility without adding it to the storage.
4035
4036        :param Solution solution: solution to store
4037        :param printreason: should all reasons of violations be printed? (Default value = True)
4038        :param completely: should all violation be checked? (Default value = False)
4039        :param checkbounds: should the bounds of the variables be checked? (Default value = True)
4040        :param checkintegrality: has integrality to be checked? (Default value = True)
4041        :param checklprows: have current LP rows (both local and global) to be checked? (Default value = True)
4042        :param original: must the solution be checked against the original problem (Default value = False)
4043
4044        """
4045        cdef SCIP_Bool feasible
4046        if original:
4047            PY_SCIP_CALL(SCIPcheckSolOrig(self._scip, solution.sol, &feasible, printreason, completely))
4048        else:
4049            PY_SCIP_CALL(SCIPcheckSol(self._scip, solution.sol, printreason, completely, checkbounds, checkintegrality, checklprows, &feasible))
4050        return feasible
4051
4052    def addSol(self, Solution solution, free=True):
4053        """Try to add a solution to the storage.
4054
4055        :param Solution solution: solution to store
4056        :param free: should solution be freed afterwards? (Default value = True)
4057
4058        """
4059        cdef SCIP_Bool stored
4060        if free:
4061            PY_SCIP_CALL(SCIPaddSolFree(self._scip, &solution.sol, &stored))
4062        else:
4063            PY_SCIP_CALL(SCIPaddSol(self._scip, solution.sol, &stored))
4064        return stored
4065
4066    def freeSol(self, Solution solution):
4067        """Free given solution
4068
4069        :param Solution solution: solution to be freed
4070
4071        """
4072        PY_SCIP_CALL(SCIPfreeSol(self._scip, &solution.sol))
4073
4074    def getSols(self):
4075        """Retrieve list of all feasible primal solutions stored in the solution storage."""
4076        cdef SCIP_SOL** _sols
4077        cdef SCIP_SOL* _sol
4078        _sols = SCIPgetSols(self._scip)
4079        nsols = SCIPgetNSols(self._scip)
4080        sols = []
4081
4082        for i in range(nsols):
4083            sols.append(Solution.create(self._scip, _sols[i]))
4084
4085        return sols
4086
4087    def getBestSol(self):
4088        """Retrieve currently best known feasible primal solution."""
4089        self._bestSol = Solution.create(self._scip, SCIPgetBestSol(self._scip))
4090        return self._bestSol
4091
4092    def getSolObjVal(self, Solution sol, original=True):
4093        """Retrieve the objective value of the solution.
4094
4095        :param Solution sol: solution
4096        :param original: objective value in original space (Default value = True)
4097
4098        """
4099        if sol == None:
4100            sol = Solution.create(self._scip, NULL)
4101        if original:
4102            objval = SCIPgetSolOrigObj(self._scip, sol.sol)
4103        else:
4104            objval = SCIPgetSolTransObj(self._scip, sol.sol)
4105        return objval
4106
4107    def getObjVal(self, original=True):
4108        """Retrieve the objective value of value of best solution.
4109        Can only be called after solving is completed.
4110
4111        :param original: objective value in original space (Default value = True)
4112
4113        """
4114        if not self.getStage() >= SCIP_STAGE_SOLVING:
4115            raise Warning("method cannot be called before problem is solved")
4116        return self.getSolObjVal(self._bestSol, original)
4117
4118    def getSolVal(self, Solution sol, Expr expr):
4119        """Retrieve value of given variable or expression in the given solution or in
4120        the LP/pseudo solution if sol == None
4121
4122        :param Solution sol: solution
4123        :param Expr expr: polynomial expression to query the value of
4124
4125        Note: a variable is also an expression
4126        """
4127        if sol == None:
4128            sol = Solution.create(self._scip, NULL)
4129        if isinstance(expr, Variable):
4130            var = <Variable> expr
4131            return SCIPgetSolVal(self._scip, sol.sol, var.scip_var)
4132        else:
4133            return expr._evaluate(sol)
4134
4135    def getVal(self, Expr expr):
4136        """Retrieve the value of the given variable or expression in the best known solution.
4137        Can only be called after solving is completed.
4138
4139        :param Expr expr: polynomial expression to query the value of
4140
4141        Note: a variable is also an expression
4142        """
4143        if not self.getStage() >= SCIP_STAGE_SOLVING:
4144            raise Warning("method cannot be called before problem is solved")
4145        return self.getSolVal(self._bestSol, expr)
4146
4147    def getPrimalbound(self):
4148        """Retrieve the best primal bound."""
4149        return SCIPgetPrimalbound(self._scip)
4150
4151    def getDualbound(self):
4152        """Retrieve the best dual bound."""
4153        return SCIPgetDualbound(self._scip)
4154
4155    def getDualboundRoot(self):
4156        """Retrieve the best root dual bound."""
4157        return SCIPgetDualboundRoot(self._scip)
4158
4159    def writeName(self, Variable var):
4160        """Write the name of the variable to the std out.
4161
4162        :param Variable var: variable
4163
4164        """
4165        PY_SCIP_CALL(SCIPwriteVarName(self._scip, NULL, var.scip_var, False))
4166
4167    def getStage(self):
4168        """Retrieve current SCIP stage"""
4169        return SCIPgetStage(self._scip)
4170
4171    def getStatus(self):
4172        """Retrieve solution status."""
4173        cdef SCIP_STATUS stat = SCIPgetStatus(self._scip)
4174        if stat == SCIP_STATUS_OPTIMAL:
4175            return "optimal"
4176        elif stat == SCIP_STATUS_TIMELIMIT:
4177            return "timelimit"
4178        elif stat == SCIP_STATUS_INFEASIBLE:
4179            return "infeasible"
4180        elif stat == SCIP_STATUS_UNBOUNDED:
4181            return "unbounded"
4182        elif stat == SCIP_STATUS_USERINTERRUPT:
4183            return "userinterrupt"
4184        elif stat == SCIP_STATUS_INFORUNBD:
4185            return "inforunbd"
4186        elif stat == SCIP_STATUS_NODELIMIT:
4187            return "nodelimit"
4188        elif stat == SCIP_STATUS_TOTALNODELIMIT:
4189            return "totalnodelimit"
4190        elif stat == SCIP_STATUS_STALLNODELIMIT:
4191            return "stallnodelimit"
4192        elif stat == SCIP_STATUS_GAPLIMIT:
4193            return "gaplimit"
4194        elif stat == SCIP_STATUS_MEMLIMIT:
4195            return "memlimit"
4196        elif stat == SCIP_STATUS_SOLLIMIT:
4197            return "sollimit"
4198        elif stat == SCIP_STATUS_BESTSOLLIMIT:
4199            return "bestsollimit"
4200        elif stat == SCIP_STATUS_RESTARTLIMIT:
4201            return  "restartlimit"
4202        else:
4203            return "unknown"
4204
4205    def getObjectiveSense(self):
4206        """Retrieve objective sense."""
4207        cdef SCIP_OBJSENSE sense = SCIPgetObjsense(self._scip)
4208        if sense == SCIP_OBJSENSE_MAXIMIZE:
4209            return "maximize"
4210        elif sense == SCIP_OBJSENSE_MINIMIZE:
4211            return "minimize"
4212        else:
4213            return "unknown"
4214
4215    def catchEvent(self, eventtype, Eventhdlr eventhdlr):
4216        """catches a global (not variable or row dependent) event"""
4217        cdef SCIP_EVENTHDLR* _eventhdlr
4218        if isinstance(eventhdlr, Eventhdlr):
4219            n = str_conversion(eventhdlr.name)
4220            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4221        else:
4222            raise Warning("event handler not found")
4223        PY_SCIP_CALL(SCIPcatchEvent(self._scip, eventtype, _eventhdlr, NULL, NULL))
4224
4225    def dropEvent(self, eventtype, Eventhdlr eventhdlr):
4226        """drops a global event (stops to track event)"""
4227        cdef SCIP_EVENTHDLR* _eventhdlr
4228        if isinstance(eventhdlr, Eventhdlr):
4229            n = str_conversion(eventhdlr.name)
4230            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4231        else:
4232            raise Warning("event handler not found")
4233        PY_SCIP_CALL(SCIPdropEvent(self._scip, eventtype, _eventhdlr, NULL, -1))
4234
4235    def catchVarEvent(self, Variable var, eventtype, Eventhdlr eventhdlr):
4236        """catches an objective value or domain change event on the given transformed variable"""
4237        cdef SCIP_EVENTHDLR* _eventhdlr
4238        if isinstance(eventhdlr, Eventhdlr):
4239            n = str_conversion(eventhdlr.name)
4240            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4241        else:
4242            raise Warning("event handler not found")
4243        PY_SCIP_CALL(SCIPcatchVarEvent(self._scip, var.scip_var, eventtype, _eventhdlr, NULL, NULL))
4244
4245    def dropVarEvent(self, Variable var, eventtype, Eventhdlr eventhdlr):
4246        """drops an objective value or domain change event (stops to track event) on the given transformed variable"""
4247        cdef SCIP_EVENTHDLR* _eventhdlr
4248        if isinstance(eventhdlr, Eventhdlr):
4249            n = str_conversion(eventhdlr.name)
4250            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4251        else:
4252            raise Warning("event handler not found")
4253        PY_SCIP_CALL(SCIPdropVarEvent(self._scip, var.scip_var, eventtype, _eventhdlr, NULL, -1))
4254
4255    def catchRowEvent(self, Row row, eventtype, Eventhdlr eventhdlr):
4256        """catches a row coefficient, constant, or side change event on the given row"""
4257        cdef SCIP_EVENTHDLR* _eventhdlr
4258        if isinstance(eventhdlr, Eventhdlr):
4259            n = str_conversion(eventhdlr.name)
4260            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4261        else:
4262            raise Warning("event handler not found")
4263        PY_SCIP_CALL(SCIPcatchRowEvent(self._scip, row.scip_row, eventtype, _eventhdlr, NULL, NULL))
4264
4265    def dropRowEvent(self, Row row, eventtype, Eventhdlr eventhdlr):
4266        """drops a row coefficient, constant, or side change event (stops to track event) on the given row"""
4267        cdef SCIP_EVENTHDLR* _eventhdlr
4268        if isinstance(eventhdlr, Eventhdlr):
4269            n = str_conversion(eventhdlr.name)
4270            _eventhdlr = SCIPfindEventhdlr(self._scip, n)
4271        else:
4272            raise Warning("event handler not found")
4273        PY_SCIP_CALL(SCIPdropRowEvent(self._scip, row.scip_row, eventtype, _eventhdlr, NULL, -1))
4274
4275    # Statistic Methods
4276
4277    def printStatistics(self):
4278        """Print statistics."""
4279        PY_SCIP_CALL(SCIPprintStatistics(self._scip, NULL))
4280
4281    def writeStatistics(self, filename="origprob.stats"):
4282      """Write statistics to a file.
4283
4284      Keyword arguments:
4285      filename -- name of the output file
4286      """
4287      # use this doubled opening pattern to ensure that IOErrors are
4288      #   triggered early and in Python not in C,Cython or SCIP.
4289      with open(filename, "w") as f:
4290          cfile = fdopen(f.fileno(), "w")
4291          PY_SCIP_CALL(SCIPprintStatistics(self._scip, cfile))
4292
4293    def getNLPs(self):
4294        """gets total number of LPs solved so far"""
4295        return SCIPgetNLPs(self._scip)
4296
4297    # Verbosity Methods
4298
4299    def hideOutput(self, quiet = True):
4300        """Hide the output.
4301
4302        :param quiet: hide output? (Default value = True)
4303
4304        """
4305        SCIPsetMessagehdlrQuiet(self._scip, quiet)
4306
4307    # Output Methods
4308
4309    def redirectOutput(self):
4310        """Send output to python instead of terminal."""
4311
4312        cdef SCIP_MESSAGEHDLR *myMessageHandler
4313
4314        PY_SCIP_CALL(SCIPmessagehdlrCreate(&myMessageHandler, False, NULL, False, relayMessage, relayMessage, relayMessage, NULL, NULL))
4315        PY_SCIP_CALL(SCIPsetMessagehdlr(self._scip, myMessageHandler))
4316        SCIPmessageSetErrorPrinting(relayErrorMessage, NULL)
4317
4318    # Parameter Methods
4319
4320    def setBoolParam(self, name, value):
4321        """Set a boolean-valued parameter.
4322
4323        :param name: name of parameter
4324        :param value: value of parameter
4325
4326        """
4327        n = str_conversion(name)
4328        PY_SCIP_CALL(SCIPsetBoolParam(self._scip, n, value))
4329
4330    def setIntParam(self, name, value):
4331        """Set an int-valued parameter.
4332
4333        :param name: name of parameter
4334        :param value: value of parameter
4335
4336        """
4337        n = str_conversion(name)
4338        PY_SCIP_CALL(SCIPsetIntParam(self._scip, n, value))
4339
4340    def setLongintParam(self, name, value):
4341        """Set a long-valued parameter.
4342
4343        :param name: name of parameter
4344        :param value: value of parameter
4345
4346        """
4347        n = str_conversion(name)
4348        PY_SCIP_CALL(SCIPsetLongintParam(self._scip, n, value))
4349
4350    def setRealParam(self, name, value):
4351        """Set a real-valued parameter.
4352
4353        :param name: name of parameter
4354        :param value: value of parameter
4355
4356        """
4357        n = str_conversion(name)
4358        PY_SCIP_CALL(SCIPsetRealParam(self._scip, n, value))
4359
4360    def setCharParam(self, name, value):
4361        """Set a char-valued parameter.
4362
4363        :param name: name of parameter
4364        :param value: value of parameter
4365
4366        """
4367        n = str_conversion(name)
4368        PY_SCIP_CALL(SCIPsetCharParam(self._scip, n, ord(value)))
4369
4370    def setStringParam(self, name, value):
4371        """Set a string-valued parameter.
4372
4373        :param name: name of parameter
4374        :param value: value of parameter
4375
4376        """
4377        n = str_conversion(name)
4378        v = str_conversion(value)
4379        PY_SCIP_CALL(SCIPsetStringParam(self._scip, n, v))
4380
4381    def setParam(self, name, value):
4382        """Set a parameter with value in int, bool, real, long, char or str.
4383
4384        :param name: name of parameter
4385        :param value: value of parameter
4386        """
4387        cdef SCIP_PARAM* param
4388
4389        n = str_conversion(name)
4390        param = SCIPgetParam(self._scip, n)
4391
4392        if param == NULL:
4393            raise KeyError("Not a valid parameter name")
4394
4395        paramtype =  SCIPparamGetType(param)
4396
4397        if paramtype == SCIP_PARAMTYPE_BOOL:
4398            PY_SCIP_CALL(SCIPsetBoolParam(self._scip, n, bool(int(value))))
4399        elif paramtype == SCIP_PARAMTYPE_INT:
4400            PY_SCIP_CALL(SCIPsetIntParam(self._scip, n, int(value)))
4401        elif paramtype == SCIP_PARAMTYPE_LONGINT:
4402            PY_SCIP_CALL(SCIPsetLongintParam(self._scip, n, int(value)))
4403        elif paramtype == SCIP_PARAMTYPE_REAL:
4404            PY_SCIP_CALL(SCIPsetRealParam(self._scip, n, float(value)))
4405        elif paramtype == SCIP_PARAMTYPE_CHAR:
4406            PY_SCIP_CALL(SCIPsetCharParam(self._scip, n, ord(value)))
4407        elif paramtype == SCIP_PARAMTYPE_STRING:
4408            v = str_conversion(value)
4409            PY_SCIP_CALL(SCIPsetStringParam(self._scip, n, v))
4410
4411
4412    def getParam(self, name):
4413        """Get the value of a parameter of type
4414        int, bool, real, long, char or str.
4415
4416        :param name: name of parameter
4417        """
4418        cdef SCIP_PARAM* param
4419
4420        n = str_conversion(name)
4421        param = SCIPgetParam(self._scip, n)
4422
4423        if param == NULL:
4424            raise KeyError("Not a valid parameter name")
4425
4426        paramtype =  SCIPparamGetType(param)
4427
4428        if paramtype == SCIP_PARAMTYPE_BOOL:
4429            return SCIPparamGetBool(param)
4430        elif paramtype == SCIP_PARAMTYPE_INT:
4431            return SCIPparamGetInt(param)
4432        elif paramtype == SCIP_PARAMTYPE_LONGINT:
4433            return SCIPparamGetLongint(param)
4434        elif paramtype == SCIP_PARAMTYPE_REAL:
4435            return SCIPparamGetReal(param)
4436        elif paramtype == SCIP_PARAMTYPE_CHAR:
4437            return chr(SCIPparamGetChar(param))
4438        elif paramtype == SCIP_PARAMTYPE_STRING:
4439            return SCIPparamGetString(param).decode('utf-8')
4440
4441    def getParams(self):
4442        """Gets the values of all parameters as a dict mapping parameter names
4443        to their values."""
4444        cdef SCIP_PARAM** params
4445
4446        params = SCIPgetParams(self._scip)
4447        result = {}
4448        for i in range(SCIPgetNParams(self._scip)):
4449          name = SCIPparamGetName(params[i]).decode('utf-8')
4450          result[name] = self.getParam(name)
4451        return result
4452
4453    def setParams(self, params):
4454        """Sets multiple parameters at once.
4455
4456        :param params: dict mapping parameter names to their values.
4457        """
4458        for name, value in params.items():
4459          self.setParam(name, value)
4460
4461    def readParams(self, file):
4462        """Read an external parameter file.
4463
4464        :param file: file to be read
4465
4466        """
4467        absfile = str_conversion(abspath(file))
4468        PY_SCIP_CALL(SCIPreadParams(self._scip, absfile))
4469
4470    def writeParams(self, filename='param.set', comments = True, onlychanged = True):
4471        """Write parameter settings to an external file.
4472
4473        :param filename: file to be written (Default value = 'param.set')
4474        :param comments: write parameter descriptions as comments? (Default value = True)
4475        :param onlychanged: write only modified parameters (Default value = True)
4476
4477        """
4478        fn = str_conversion(filename)
4479        PY_SCIP_CALL(SCIPwriteParams(self._scip, fn, comments, onlychanged))
4480        print('wrote parameter settings to file ' + filename)
4481
4482    def resetParam(self, name):
4483        """Reset parameter setting to its default value
4484
4485        :param name: parameter to reset
4486
4487        """
4488        n = str_conversion(name)
4489        PY_SCIP_CALL(SCIPresetParam(self._scip, n))
4490
4491    def resetParams(self):
4492        """Reset parameter settings to their default values"""
4493        PY_SCIP_CALL(SCIPresetParams(self._scip))
4494
4495    def setEmphasis(self, paraemphasis, quiet = True):
4496        """Set emphasis settings
4497
4498        :param paraemphasis: emphasis to set
4499        :param quiet: hide output? (Default value = True)
4500
4501        """
4502        PY_SCIP_CALL(SCIPsetEmphasis(self._scip, paraemphasis, quiet))
4503
4504    def readProblem(self, filename, extension = None):
4505        """Read a problem instance from an external file.
4506
4507        :param filename: problem file name
4508        :param extension: specify file extension/type (Default value = None)
4509
4510        """
4511        absfile = str_conversion(abspath(filename))
4512        if extension is None:
4513            PY_SCIP_CALL(SCIPreadProb(self._scip, absfile, NULL))
4514        else:
4515            extension = str_conversion(extension)
4516            PY_SCIP_CALL(SCIPreadProb(self._scip, absfile, extension))
4517
4518    # Counting functions
4519
4520    def count(self):
4521        """Counts the number of feasible points of problem."""
4522        PY_SCIP_CALL(SCIPcount(self._scip))
4523
4524    def getNCountedSols(self):
4525        """Get number of feasible solution."""
4526        cdef SCIP_Bool valid
4527        cdef SCIP_Longint nsols
4528
4529        nsols = SCIPgetNCountedSols(self._scip, &valid)
4530        if not valid:
4531            print('total number of solutions found is not valid!')
4532        return nsols
4533
4534    def setParamsCountsols(self):
4535        """Sets SCIP parameters such that a valid counting process is possible."""
4536        PY_SCIP_CALL(SCIPsetParamsCountsols(self._scip))
4537
4538    def freeReoptSolve(self):
4539        """Frees all solution process data and prepares for reoptimization"""
4540        PY_SCIP_CALL(SCIPfreeReoptSolve(self._scip))
4541
4542    def chgReoptObjective(self, coeffs, sense = 'minimize'):
4543        """Establish the objective function as a linear expression.
4544
4545        :param coeffs: the coefficients
4546        :param sense: the objective sense (Default value = 'minimize')
4547
4548        """
4549
4550        cdef SCIP_OBJSENSE objsense
4551
4552        if sense == "minimize":
4553            objsense = SCIP_OBJSENSE_MINIMIZE
4554        elif sense == "maximize":
4555            objsense = SCIP_OBJSENSE_MAXIMIZE
4556        else:
4557            raise Warning("unrecognized optimization sense: %s" % sense)
4558
4559        assert isinstance(coeffs, Expr), "given coefficients are not Expr but %s" % coeffs.__class__.__name__
4560
4561        if coeffs.degree() > 1:
4562            raise ValueError("Nonlinear objective functions are not supported!")
4563        if coeffs[CONST] != 0.0:
4564            raise ValueError("Constant offsets in objective are not supported!")
4565
4566        cdef SCIP_VAR** _vars
4567        cdef int _nvars
4568        _vars = SCIPgetOrigVars(self._scip)
4569        _nvars = SCIPgetNOrigVars(self._scip)
4570        _coeffs = <SCIP_Real*> malloc(_nvars * sizeof(SCIP_Real))
4571
4572        for i in range(_nvars):
4573            _coeffs[i] = 0.0
4574
4575        for term, coef in coeffs.terms.items():
4576            # avoid CONST term of Expr
4577            if term != CONST:
4578                assert len(term) == 1
4579                var = <Variable>term[0]
4580                for i in range(_nvars):
4581                    if _vars[i] == var.scip_var:
4582                        _coeffs[i] = coef
4583
4584        PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, _vars, &_coeffs[0], _nvars))
4585
4586        free(_coeffs)
4587
4588    def chgVarBranchPriority(self, Variable var, priority):
4589        """Sets the branch priority of the variable.
4590        Variables with higher branch priority are always preferred to variables with lower priority in selection of branching variable.
4591
4592        :param Variable var: variable to change priority of
4593        :param priority: the new priority of the variable (the default branching priority is 0)
4594        """
4595        assert isinstance(var, Variable), "The given variable is not a pyvar, but %s" % var.__class__.__name__
4596        PY_SCIP_CALL(SCIPchgVarBranchPriority(self._scip, var.scip_var, priority))
4597
4598# debugging memory management
4599def is_memory_freed():
4600    return BMSgetMemoryUsed() == 0
4601
4602def print_memory_in_use():
4603    BMScheckEmptyMemory()
4604