-
Notifications
You must be signed in to change notification settings - Fork 252
/
scip.pxi
5284 lines (4220 loc) · 216 KB
/
scip.pxi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
##@file scip.pxi
#@brief holding functions in python that reference the SCIP public functions included in scip.pxd
import weakref
from os.path import abspath
from os.path import splitext
import os
import sys
import warnings
import locale
cimport cython
from cpython cimport Py_INCREF, Py_DECREF
from cpython.pycapsule cimport PyCapsule_New, PyCapsule_IsValid, PyCapsule_GetPointer
from libc.stdlib cimport malloc, free
from libc.stdio cimport fdopen, fclose
from posix.stdio cimport fileno
from collections.abc import Iterable
from itertools import repeat
include "expr.pxi"
include "lp.pxi"
include "benders.pxi"
include "benderscut.pxi"
include "branchrule.pxi"
include "conshdlr.pxi"
include "cutsel.pxi"
include "event.pxi"
include "heuristic.pxi"
include "presol.pxi"
include "pricer.pxi"
include "propagator.pxi"
include "sepa.pxi"
include "reader.pxi"
include "relax.pxi"
include "nodesel.pxi"
# recommended SCIP version; major version is required
MAJOR = 9
MINOR = 0
PATCH = 0
# for external user functions use def; for functions used only inside the interface (starting with _) use cdef
# todo: check whether this is currently done like this
if sys.version_info >= (3, 0):
str_conversion = lambda x:bytes(x,'utf-8')
else:
str_conversion = lambda x:x
_SCIP_BOUNDTYPE_TO_STRING = {SCIP_BOUNDTYPE_UPPER: '<=',
SCIP_BOUNDTYPE_LOWER: '>='}
# Mapping the SCIP_RESULT enum to a python class
# This is required to return SCIP_RESULT in the python code
# In __init__.py this is imported as SCIP_RESULT to keep the
# original naming scheme using capital letters
cdef class PY_SCIP_RESULT:
DIDNOTRUN = SCIP_DIDNOTRUN
DELAYED = SCIP_DELAYED
DIDNOTFIND = SCIP_DIDNOTFIND
FEASIBLE = SCIP_FEASIBLE
INFEASIBLE = SCIP_INFEASIBLE
UNBOUNDED = SCIP_UNBOUNDED
CUTOFF = SCIP_CUTOFF
SEPARATED = SCIP_SEPARATED
NEWROUND = SCIP_NEWROUND
REDUCEDDOM = SCIP_REDUCEDDOM
CONSADDED = SCIP_CONSADDED
CONSCHANGED = SCIP_CONSCHANGED
BRANCHED = SCIP_BRANCHED
SOLVELP = SCIP_SOLVELP
FOUNDSOL = SCIP_FOUNDSOL
SUSPENDED = SCIP_SUSPENDED
SUCCESS = SCIP_SUCCESS
cdef class PY_SCIP_PARAMSETTING:
DEFAULT = SCIP_PARAMSETTING_DEFAULT
AGGRESSIVE = SCIP_PARAMSETTING_AGGRESSIVE
FAST = SCIP_PARAMSETTING_FAST
OFF = SCIP_PARAMSETTING_OFF
cdef class PY_SCIP_PARAMEMPHASIS:
DEFAULT = SCIP_PARAMEMPHASIS_DEFAULT
CPSOLVER = SCIP_PARAMEMPHASIS_CPSOLVER
EASYCIP = SCIP_PARAMEMPHASIS_EASYCIP
FEASIBILITY = SCIP_PARAMEMPHASIS_FEASIBILITY
HARDLP = SCIP_PARAMEMPHASIS_HARDLP
OPTIMALITY = SCIP_PARAMEMPHASIS_OPTIMALITY
COUNTER = SCIP_PARAMEMPHASIS_COUNTER
PHASEFEAS = SCIP_PARAMEMPHASIS_PHASEFEAS
PHASEIMPROVE = SCIP_PARAMEMPHASIS_PHASEIMPROVE
PHASEPROOF = SCIP_PARAMEMPHASIS_PHASEPROOF
NUMERICS = SCIP_PARAMEMPHASIS_NUMERICS
BENCHMARK = SCIP_PARAMEMPHASIS_BENCHMARK
cdef class PY_SCIP_STATUS:
UNKNOWN = SCIP_STATUS_UNKNOWN
USERINTERRUPT = SCIP_STATUS_USERINTERRUPT
NODELIMIT = SCIP_STATUS_NODELIMIT
TOTALNODELIMIT = SCIP_STATUS_TOTALNODELIMIT
STALLNODELIMIT = SCIP_STATUS_STALLNODELIMIT
TIMELIMIT = SCIP_STATUS_TIMELIMIT
MEMLIMIT = SCIP_STATUS_MEMLIMIT
GAPLIMIT = SCIP_STATUS_GAPLIMIT
SOLLIMIT = SCIP_STATUS_SOLLIMIT
BESTSOLLIMIT = SCIP_STATUS_BESTSOLLIMIT
RESTARTLIMIT = SCIP_STATUS_RESTARTLIMIT
OPTIMAL = SCIP_STATUS_OPTIMAL
INFEASIBLE = SCIP_STATUS_INFEASIBLE
UNBOUNDED = SCIP_STATUS_UNBOUNDED
INFORUNBD = SCIP_STATUS_INFORUNBD
StageNames = {}
cdef class PY_SCIP_STAGE:
INIT = SCIP_STAGE_INIT
PROBLEM = SCIP_STAGE_PROBLEM
TRANSFORMING = SCIP_STAGE_TRANSFORMING
TRANSFORMED = SCIP_STAGE_TRANSFORMED
INITPRESOLVE = SCIP_STAGE_INITPRESOLVE
PRESOLVING = SCIP_STAGE_PRESOLVING
EXITPRESOLVE = SCIP_STAGE_EXITPRESOLVE
PRESOLVED = SCIP_STAGE_PRESOLVED
INITSOLVE = SCIP_STAGE_INITSOLVE
SOLVING = SCIP_STAGE_SOLVING
SOLVED = SCIP_STAGE_SOLVED
EXITSOLVE = SCIP_STAGE_EXITSOLVE
FREETRANS = SCIP_STAGE_FREETRANS
FREE = SCIP_STAGE_FREE
cdef class PY_SCIP_NODETYPE:
FOCUSNODE = SCIP_NODETYPE_FOCUSNODE
PROBINGNODE = SCIP_NODETYPE_PROBINGNODE
SIBLING = SCIP_NODETYPE_SIBLING
CHILD = SCIP_NODETYPE_CHILD
LEAF = SCIP_NODETYPE_LEAF
DEADEND = SCIP_NODETYPE_DEADEND
JUNCTION = SCIP_NODETYPE_JUNCTION
PSEUDOFORK = SCIP_NODETYPE_PSEUDOFORK
FORK = SCIP_NODETYPE_FORK
SUBROOT = SCIP_NODETYPE_SUBROOT
REFOCUSNODE = SCIP_NODETYPE_REFOCUSNODE
cdef class PY_SCIP_PROPTIMING:
BEFORELP = SCIP_PROPTIMING_BEFORELP
DURINGLPLOOP = SCIP_PROPTIMING_DURINGLPLOOP
AFTERLPLOOP = SCIP_PROPTIMING_AFTERLPLOOP
AFTERLPNODE = SCIP_PROPTIMING_AFTERLPNODE
cdef class PY_SCIP_PRESOLTIMING:
NONE = SCIP_PRESOLTIMING_NONE
FAST = SCIP_PRESOLTIMING_FAST
MEDIUM = SCIP_PRESOLTIMING_MEDIUM
EXHAUSTIVE = SCIP_PRESOLTIMING_EXHAUSTIVE
cdef class PY_SCIP_HEURTIMING:
BEFORENODE = SCIP_HEURTIMING_BEFORENODE
DURINGLPLOOP = SCIP_HEURTIMING_DURINGLPLOOP
AFTERLPLOOP = SCIP_HEURTIMING_AFTERLPLOOP
AFTERLPNODE = SCIP_HEURTIMING_AFTERLPNODE
AFTERPSEUDONODE = SCIP_HEURTIMING_AFTERPSEUDONODE
AFTERLPPLUNGE = SCIP_HEURTIMING_AFTERLPPLUNGE
AFTERPSEUDOPLUNGE = SCIP_HEURTIMING_AFTERPSEUDOPLUNGE
DURINGPRICINGLOOP = SCIP_HEURTIMING_DURINGPRICINGLOOP
BEFOREPRESOL = SCIP_HEURTIMING_BEFOREPRESOL
DURINGPRESOLLOOP = SCIP_HEURTIMING_DURINGPRESOLLOOP
AFTERPROPLOOP = SCIP_HEURTIMING_AFTERPROPLOOP
EventNames = {}
cdef class PY_SCIP_EVENTTYPE:
DISABLED = SCIP_EVENTTYPE_DISABLED
VARADDED = SCIP_EVENTTYPE_VARADDED
VARDELETED = SCIP_EVENTTYPE_VARDELETED
VARFIXED = SCIP_EVENTTYPE_VARFIXED
VARUNLOCKED = SCIP_EVENTTYPE_VARUNLOCKED
OBJCHANGED = SCIP_EVENTTYPE_OBJCHANGED
GLBCHANGED = SCIP_EVENTTYPE_GLBCHANGED
GUBCHANGED = SCIP_EVENTTYPE_GUBCHANGED
LBTIGHTENED = SCIP_EVENTTYPE_LBTIGHTENED
LBRELAXED = SCIP_EVENTTYPE_LBRELAXED
UBTIGHTENED = SCIP_EVENTTYPE_UBTIGHTENED
UBRELAXED = SCIP_EVENTTYPE_UBRELAXED
GHOLEADDED = SCIP_EVENTTYPE_GHOLEADDED
GHOLEREMOVED = SCIP_EVENTTYPE_GHOLEREMOVED
LHOLEADDED = SCIP_EVENTTYPE_LHOLEADDED
LHOLEREMOVED = SCIP_EVENTTYPE_LHOLEREMOVED
IMPLADDED = SCIP_EVENTTYPE_IMPLADDED
PRESOLVEROUND = SCIP_EVENTTYPE_PRESOLVEROUND
NODEFOCUSED = SCIP_EVENTTYPE_NODEFOCUSED
NODEFEASIBLE = SCIP_EVENTTYPE_NODEFEASIBLE
NODEINFEASIBLE = SCIP_EVENTTYPE_NODEINFEASIBLE
NODEBRANCHED = SCIP_EVENTTYPE_NODEBRANCHED
NODEDELETE = SCIP_EVENTTYPE_NODEDELETE
FIRSTLPSOLVED = SCIP_EVENTTYPE_FIRSTLPSOLVED
LPSOLVED = SCIP_EVENTTYPE_LPSOLVED
LPEVENT = SCIP_EVENTTYPE_LPEVENT
POORSOLFOUND = SCIP_EVENTTYPE_POORSOLFOUND
BESTSOLFOUND = SCIP_EVENTTYPE_BESTSOLFOUND
ROWADDEDSEPA = SCIP_EVENTTYPE_ROWADDEDSEPA
ROWDELETEDSEPA = SCIP_EVENTTYPE_ROWDELETEDSEPA
ROWADDEDLP = SCIP_EVENTTYPE_ROWADDEDLP
ROWDELETEDLP = SCIP_EVENTTYPE_ROWDELETEDLP
ROWCOEFCHANGED = SCIP_EVENTTYPE_ROWCOEFCHANGED
ROWCONSTCHANGED = SCIP_EVENTTYPE_ROWCONSTCHANGED
ROWSIDECHANGED = SCIP_EVENTTYPE_ROWSIDECHANGED
SYNC = SCIP_EVENTTYPE_SYNC
GBDCHANGED = SCIP_EVENTTYPE_GBDCHANGED
LBCHANGED = SCIP_EVENTTYPE_LBCHANGED
UBCHANGED = SCIP_EVENTTYPE_UBCHANGED
BOUNDTIGHTENED = SCIP_EVENTTYPE_BOUNDTIGHTENED
BOUNDRELAXED = SCIP_EVENTTYPE_BOUNDRELAXED
BOUNDCHANGED = SCIP_EVENTTYPE_BOUNDCHANGED
GHOLECHANGED = SCIP_EVENTTYPE_GHOLECHANGED
LHOLECHANGED = SCIP_EVENTTYPE_LHOLECHANGED
HOLECHANGED = SCIP_EVENTTYPE_HOLECHANGED
DOMCHANGED = SCIP_EVENTTYPE_DOMCHANGED
VARCHANGED = SCIP_EVENTTYPE_VARCHANGED
VAREVENT = SCIP_EVENTTYPE_VAREVENT
NODESOLVED = SCIP_EVENTTYPE_NODESOLVED
NODEEVENT = SCIP_EVENTTYPE_NODEEVENT
SOLFOUND = SCIP_EVENTTYPE_SOLFOUND
SOLEVENT = SCIP_EVENTTYPE_SOLEVENT
ROWCHANGED = SCIP_EVENTTYPE_ROWCHANGED
ROWEVENT = SCIP_EVENTTYPE_ROWEVENT
cdef class PY_SCIP_LPSOLSTAT:
NOTSOLVED = SCIP_LPSOLSTAT_NOTSOLVED
OPTIMAL = SCIP_LPSOLSTAT_OPTIMAL
INFEASIBLE = SCIP_LPSOLSTAT_INFEASIBLE
UNBOUNDEDRAY = SCIP_LPSOLSTAT_UNBOUNDEDRAY
OBJLIMIT = SCIP_LPSOLSTAT_OBJLIMIT
ITERLIMIT = SCIP_LPSOLSTAT_ITERLIMIT
TIMELIMIT = SCIP_LPSOLSTAT_TIMELIMIT
ERROR = SCIP_LPSOLSTAT_ERROR
cdef class PY_SCIP_BRANCHDIR:
DOWNWARDS = SCIP_BRANCHDIR_DOWNWARDS
UPWARDS = SCIP_BRANCHDIR_UPWARDS
FIXED = SCIP_BRANCHDIR_FIXED
AUTO = SCIP_BRANCHDIR_AUTO
cdef class PY_SCIP_BENDERSENFOTYPE:
LP = SCIP_BENDERSENFOTYPE_LP
RELAX = SCIP_BENDERSENFOTYPE_RELAX
PSEUDO = SCIP_BENDERSENFOTYPE_PSEUDO
CHECK = SCIP_BENDERSENFOTYPE_CHECK
cdef class PY_SCIP_ROWORIGINTYPE:
UNSPEC = SCIP_ROWORIGINTYPE_UNSPEC
CONS = SCIP_ROWORIGINTYPE_CONS
SEPA = SCIP_ROWORIGINTYPE_SEPA
REOPT = SCIP_ROWORIGINTYPE_REOPT
def PY_SCIP_CALL(SCIP_RETCODE rc):
if rc == SCIP_OKAY:
pass
elif rc == SCIP_ERROR:
raise Exception('SCIP: unspecified error!')
elif rc == SCIP_NOMEMORY:
raise MemoryError('SCIP: insufficient memory error!')
elif rc == SCIP_READERROR:
raise IOError('SCIP: read error!')
elif rc == SCIP_WRITEERROR:
raise IOError('SCIP: write error!')
elif rc == SCIP_NOFILE:
raise IOError('SCIP: file not found error!')
elif rc == SCIP_FILECREATEERROR:
raise IOError('SCIP: cannot create file!')
elif rc == SCIP_LPERROR:
raise Exception('SCIP: error in LP solver!')
elif rc == SCIP_NOPROBLEM:
raise Exception('SCIP: no problem exists!')
elif rc == SCIP_INVALIDCALL:
raise Exception('SCIP: method cannot be called at this time'
+ ' in solution process!')
elif rc == SCIP_INVALIDDATA:
raise Exception('SCIP: error in input data!')
elif rc == SCIP_INVALIDRESULT:
raise Exception('SCIP: method returned an invalid result code!')
elif rc == SCIP_PLUGINNOTFOUND:
raise Exception('SCIP: a required plugin was not found !')
elif rc == SCIP_PARAMETERUNKNOWN:
raise KeyError('SCIP: the parameter with the given name was not found!')
elif rc == SCIP_PARAMETERWRONGTYPE:
raise LookupError('SCIP: the parameter is not of the expected type!')
elif rc == SCIP_PARAMETERWRONGVAL:
raise ValueError('SCIP: the value is invalid for the given parameter!')
elif rc == SCIP_KEYALREADYEXISTING:
raise KeyError('SCIP: the given key is already existing in table!')
elif rc == SCIP_MAXDEPTHLEVEL:
raise Exception('SCIP: maximal branching depth level exceeded!')
else:
raise Exception('SCIP: unknown return code!')
cdef class Event:
"""Base class holding a pointer to corresponding SCIP_EVENT"""
@staticmethod
cdef create(SCIP_EVENT* scip_event):
if scip_event == NULL:
raise Warning("cannot create Event with SCIP_EVENT* == NULL")
event = Event()
event.event = scip_event
return event
def getType(self):
"""gets type of event"""
return SCIPeventGetType(self.event)
def getName(self):
"""gets name of event"""
if not EventNames:
self._getEventNames()
return EventNames[self.getType()]
def _getEventNames(self):
"""gets event names"""
for name in dir(PY_SCIP_EVENTTYPE):
attr = getattr(PY_SCIP_EVENTTYPE, name)
if isinstance(attr, int):
EventNames[attr] = name
def __repr__(self):
return str(self.getType())
def __str__(self):
return self.getName()
def getNewBound(self):
"""gets new bound for a bound change event"""
return SCIPeventGetNewbound(self.event)
def getOldBound(self):
"""gets old bound for a bound change event"""
return SCIPeventGetOldbound(self.event)
def getVar(self):
"""gets variable for a variable event (var added, var deleted, var fixed, objective value or domain change, domain hole added or removed)"""
cdef SCIP_VAR* var = SCIPeventGetVar(self.event)
return Variable.create(var)
def getNode(self):
"""gets node for a node or LP event"""
cdef SCIP_NODE* node = SCIPeventGetNode(self.event)
return Node.create(node)
def getRow(self):
"""gets row for a row event"""
cdef SCIP_ROW* row = SCIPeventGetRow(self.event)
return Row.create(row)
def __hash__(self):
return hash(<size_t>self.event)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.event == (<Event>other).event)
cdef class Column:
"""Base class holding a pointer to corresponding SCIP_COL"""
@staticmethod
cdef create(SCIP_COL* scipcol):
if scipcol == NULL:
raise Warning("cannot create Column with SCIP_COL* == NULL")
col = Column()
col.scip_col = scipcol
return col
def getLPPos(self):
"""gets position of column in current LP, or -1 if it is not in LP"""
return SCIPcolGetLPPos(self.scip_col)
def getBasisStatus(self):
"""gets the basis status of a column in the LP solution, Note: returns basis status `zero` for columns not in the current SCIP LP"""
cdef SCIP_BASESTAT stat = SCIPcolGetBasisStatus(self.scip_col)
if stat == SCIP_BASESTAT_LOWER:
return "lower"
elif stat == SCIP_BASESTAT_BASIC:
return "basic"
elif stat == SCIP_BASESTAT_UPPER:
return "upper"
elif stat == SCIP_BASESTAT_ZERO:
return "zero"
else:
raise Exception('SCIP returned unknown base status!')
def isIntegral(self):
"""returns whether the associated variable is of integral type (binary, integer, implicit integer)"""
return SCIPcolIsIntegral(self.scip_col)
def getVar(self):
"""gets variable this column represents"""
cdef SCIP_VAR* var = SCIPcolGetVar(self.scip_col)
return Variable.create(var)
def getPrimsol(self):
"""gets the primal LP solution of a column"""
return SCIPcolGetPrimsol(self.scip_col)
def getLb(self):
"""gets lower bound of column"""
return SCIPcolGetLb(self.scip_col)
def getUb(self):
"""gets upper bound of column"""
return SCIPcolGetUb(self.scip_col)
def getObjCoeff(self):
"""gets objective value coefficient of a column"""
return SCIPcolGetObj(self.scip_col)
def __hash__(self):
return hash(<size_t>self.scip_col)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.scip_col == (<Column>other).scip_col)
cdef class Row:
"""Base class holding a pointer to corresponding SCIP_ROW"""
@staticmethod
cdef create(SCIP_ROW* sciprow):
if sciprow == NULL:
raise Warning("cannot create Row with SCIP_ROW* == NULL")
row = Row()
row.scip_row = sciprow
return row
property name:
def __get__(self):
cname = bytes( SCIProwGetName(self.scip_row) )
return cname.decode('utf-8')
def getLhs(self):
"""returns the left hand side of row"""
return SCIProwGetLhs(self.scip_row)
def getRhs(self):
"""returns the right hand side of row"""
return SCIProwGetRhs(self.scip_row)
def getConstant(self):
"""gets constant shift of row"""
return SCIProwGetConstant(self.scip_row)
def getLPPos(self):
"""gets position of row in current LP, or -1 if it is not in LP"""
return SCIProwGetLPPos(self.scip_row)
def getBasisStatus(self):
"""gets the basis status of a row in the LP solution, Note: returns basis status `basic` for rows not in the current SCIP LP"""
cdef SCIP_BASESTAT stat = SCIProwGetBasisStatus(self.scip_row)
if stat == SCIP_BASESTAT_LOWER:
return "lower"
elif stat == SCIP_BASESTAT_BASIC:
return "basic"
elif stat == SCIP_BASESTAT_UPPER:
return "upper"
elif stat == SCIP_BASESTAT_ZERO:
# this shouldn't happen!
raise Exception('SCIP returned base status zero for a row!')
else:
raise Exception('SCIP returned unknown base status!')
def isIntegral(self):
"""returns TRUE iff the activity of the row (without the row's constant) is always integral in a feasible solution """
return SCIProwIsIntegral(self.scip_row)
def isLocal(self):
"""returns TRUE iff the row is only valid locally """
return SCIProwIsLocal(self.scip_row)
def isModifiable(self):
"""returns TRUE iff row is modifiable during node processing (subject to column generation) """
return SCIProwIsModifiable(self.scip_row)
def isRemovable(self):
"""returns TRUE iff row is removable from the LP (due to aging or cleanup)"""
return SCIProwIsRemovable(self.scip_row)
def isInGlobalCutpool(self):
"""return TRUE iff row is a member of the global cut pool"""
return SCIProwIsInGlobalCutpool(self.scip_row)
def getOrigintype(self):
"""returns type of origin that created the row"""
return SCIProwGetOrigintype(self.scip_row)
def getConsOriginConshdlrtype(self):
"""returns type of constraint handler that created the row"""
cdef SCIP_CONS* scip_con = SCIProwGetOriginCons(self.scip_row)
return bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(scip_con))).decode('UTF-8')
def getNNonz(self):
"""get number of nonzero entries in row vector"""
return SCIProwGetNNonz(self.scip_row)
def getNLPNonz(self):
"""get number of nonzero entries in row vector that correspond to columns currently in the SCIP LP"""
return SCIProwGetNLPNonz(self.scip_row)
def getCols(self):
"""gets list with columns of nonzero entries"""
cdef SCIP_COL** cols = SCIProwGetCols(self.scip_row)
return [Column.create(cols[i]) for i in range(self.getNNonz())]
def getVals(self):
"""gets list with coefficients of nonzero entries"""
cdef SCIP_Real* vals = SCIProwGetVals(self.scip_row)
return [vals[i] for i in range(self.getNNonz())]
def getNorm(self):
"""gets Euclidean norm of row vector """
return SCIProwGetNorm(self.scip_row)
def __hash__(self):
return hash(<size_t>self.scip_row)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.scip_row == (<Row>other).scip_row)
cdef class NLRow:
"""Base class holding a pointer to corresponding SCIP_NLROW"""
@staticmethod
cdef create(SCIP_NLROW* scipnlrow):
if scipnlrow == NULL:
raise Warning("cannot create NLRow with SCIP_NLROW* == NULL")
nlrow = NLRow()
nlrow.scip_nlrow = scipnlrow
return nlrow
property name:
def __get__(self):
cname = bytes( SCIPnlrowGetName(self.scip_nlrow) )
return cname.decode('utf-8')
def getConstant(self):
"""returns the constant of a nonlinear row"""
return SCIPnlrowGetConstant(self.scip_nlrow)
def getLinearTerms(self):
"""returns a list of tuples (var, coef) representing the linear part of a nonlinear row"""
cdef SCIP_VAR** linvars = SCIPnlrowGetLinearVars(self.scip_nlrow)
cdef SCIP_Real* lincoefs = SCIPnlrowGetLinearCoefs(self.scip_nlrow)
cdef int nlinvars = SCIPnlrowGetNLinearVars(self.scip_nlrow)
return [(Variable.create(linvars[i]), lincoefs[i]) for i in range(nlinvars)]
def getLhs(self):
"""returns the left hand side of a nonlinear row"""
return SCIPnlrowGetLhs(self.scip_nlrow)
def getRhs(self):
"""returns the right hand side of a nonlinear row"""
return SCIPnlrowGetRhs(self.scip_nlrow)
def getDualsol(self):
"""gets the dual NLP solution of a nonlinear row"""
return SCIPnlrowGetDualsol(self.scip_nlrow)
def __hash__(self):
return hash(<size_t>self.scip_nlrow)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.scip_nlrow == (<NLRow>other).scip_nlrow)
cdef class Solution:
"""Base class holding a pointer to corresponding SCIP_SOL"""
# We are raising an error here to avoid creating a solution without an associated model. See Issue #625
def __init__(self, raise_error = False):
if not raise_error:
raise ValueError("To create a solution you should use the createSol method of the Model class.")
@staticmethod
cdef create(SCIP* scip, SCIP_SOL* scip_sol):
if scip == NULL:
raise Warning("cannot create Solution with SCIP* == NULL")
sol = Solution(True)
sol.sol = scip_sol
sol.scip = scip
return sol
def __getitem__(self, Expr expr):
# fast track for Variable
if isinstance(expr, Variable):
self._checkStage("SCIPgetSolVal")
var = <Variable> expr
return SCIPgetSolVal(self.scip, self.sol, var.scip_var)
return sum(self._evaluate(term)*coeff for term, coeff in expr.terms.items() if coeff != 0)
def _evaluate(self, term):
self._checkStage("SCIPgetSolVal")
result = 1
for var in term.vartuple:
result *= SCIPgetSolVal(self.scip, self.sol, (<Variable> var).scip_var)
return result
def __setitem__(self, Variable var, value):
PY_SCIP_CALL(SCIPsetSolVal(self.scip, self.sol, var.scip_var, value))
def __repr__(self):
cdef SCIP_VAR* scip_var
vals = {}
self._checkStage("SCIPgetSolVal")
for i in range(SCIPgetNOrigVars(self.scip)):
scip_var = SCIPgetOrigVars(self.scip)[i]
# extract name
cname = bytes(SCIPvarGetName(scip_var))
name = cname.decode('utf-8')
vals[name] = SCIPgetSolVal(self.scip, self.sol, scip_var)
return str(vals)
def _checkStage(self, method):
if method in ["SCIPgetSolVal", "getSolObjVal"]:
if self.sol == NULL and SCIPgetStage(self.scip) != SCIP_STAGE_SOLVING:
raise Warning(f"{method} can only be called with a valid solution or in stage SOLVING (current stage: {SCIPgetStage(self.scip)})")
cdef class BoundChange:
"""Bound change."""
@staticmethod
cdef create(SCIP_BOUNDCHG* scip_boundchg):
if scip_boundchg == NULL:
raise Warning("cannot create BoundChange with SCIP_BOUNDCHG* == NULL")
boundchg = BoundChange()
boundchg.scip_boundchg = scip_boundchg
return boundchg
def getNewBound(self):
"""Returns the new value of the bound in the bound change."""
return SCIPboundchgGetNewbound(self.scip_boundchg)
def getVar(self):
"""Returns the variable of the bound change."""
return Variable.create(SCIPboundchgGetVar(self.scip_boundchg))
def getBoundchgtype(self):
"""Returns the bound change type of the bound change."""
return SCIPboundchgGetBoundchgtype(self.scip_boundchg)
def getBoundtype(self):
"""Returns the bound type of the bound change."""
return SCIPboundchgGetBoundtype(self.scip_boundchg)
def isRedundant(self):
"""Returns whether the bound change is redundant due to a more global bound that is at least as strong."""
return SCIPboundchgIsRedundant(self.scip_boundchg)
def __repr__(self):
return "{} {} {}".format(self.getVar(),
_SCIP_BOUNDTYPE_TO_STRING[self.getBoundtype()],
self.getNewBound())
cdef class DomainChanges:
"""Set of domain changes."""
@staticmethod
cdef create(SCIP_DOMCHG* scip_domchg):
if scip_domchg == NULL:
raise Warning("cannot create DomainChanges with SCIP_DOMCHG* == NULL")
domchg = DomainChanges()
domchg.scip_domchg = scip_domchg
return domchg
def getBoundchgs(self):
"""Returns the bound changes in the domain change."""
nboundchgs = SCIPdomchgGetNBoundchgs(self.scip_domchg)
return [BoundChange.create(SCIPdomchgGetBoundchg(self.scip_domchg, i))
for i in range(nboundchgs)]
cdef class Node:
"""Base class holding a pointer to corresponding SCIP_NODE"""
@staticmethod
cdef create(SCIP_NODE* scipnode):
if scipnode == NULL:
return None
node = Node()
node.scip_node = scipnode
return node
def getParent(self):
"""Retrieve parent node (or None if the node has no parent node)."""
return Node.create(SCIPnodeGetParent(self.scip_node))
def getNumber(self):
"""Retrieve number of node."""
return SCIPnodeGetNumber(self.scip_node)
def getDepth(self):
"""Retrieve depth of node."""
return SCIPnodeGetDepth(self.scip_node)
def getType(self):
"""Retrieve type of node."""
return SCIPnodeGetType(self.scip_node)
def getLowerbound(self):
"""Retrieve lower bound of node."""
return SCIPnodeGetLowerbound(self.scip_node)
def getEstimate(self):
"""Retrieve the estimated value of the best feasible solution in subtree of the node"""
return SCIPnodeGetEstimate(self.scip_node)
def getAddedConss(self):
"""Retrieve all constraints added at this node."""
cdef int addedconsssize = SCIPnodeGetNAddedConss(self.scip_node)
if addedconsssize == 0:
return []
cdef SCIP_CONS** addedconss = <SCIP_CONS**> malloc(addedconsssize * sizeof(SCIP_CONS*))
cdef int nconss
SCIPnodeGetAddedConss(self.scip_node, addedconss, &nconss, addedconsssize)
assert nconss == addedconsssize
constraints = [Constraint.create(addedconss[i]) for i in range(nconss)]
free(addedconss)
return constraints
def getNAddedConss(self):
"""Retrieve number of added constraints at this node"""
return SCIPnodeGetNAddedConss(self.scip_node)
def isActive(self):
"""Is the node in the path to the current node?"""
return SCIPnodeIsActive(self.scip_node)
def isPropagatedAgain(self):
"""Is the node marked to be propagated again?"""
return SCIPnodeIsPropagatedAgain(self.scip_node)
def getNParentBranchings(self):
"""Retrieve the number of variable branchings that were performed in the parent node to create this node."""
cdef SCIP_VAR* dummy_branchvars
cdef SCIP_Real dummy_branchbounds
cdef SCIP_BOUNDTYPE dummy_boundtypes
cdef int nbranchvars
# This is a hack: the SCIP interface has no function to directly get the
# number of parent branchings, i.e., SCIPnodeGetNParentBranchings() does
# not exist.
SCIPnodeGetParentBranchings(self.scip_node, &dummy_branchvars,
&dummy_branchbounds, &dummy_boundtypes,
&nbranchvars, 0)
return nbranchvars
def getParentBranchings(self):
"""Retrieve the set of variable branchings that were performed in the parent node to create this node."""
cdef int nbranchvars = self.getNParentBranchings()
if nbranchvars == 0:
return None
cdef SCIP_VAR** branchvars = <SCIP_VAR**> malloc(nbranchvars * sizeof(SCIP_VAR*))
cdef SCIP_Real* branchbounds = <SCIP_Real*> malloc(nbranchvars * sizeof(SCIP_Real))
cdef SCIP_BOUNDTYPE* boundtypes = <SCIP_BOUNDTYPE*> malloc(nbranchvars * sizeof(SCIP_BOUNDTYPE))
SCIPnodeGetParentBranchings(self.scip_node, branchvars, branchbounds,
boundtypes, &nbranchvars, nbranchvars)
py_variables = [Variable.create(branchvars[i]) for i in range(nbranchvars)]
py_branchbounds = [branchbounds[i] for i in range(nbranchvars)]
py_boundtypes = [boundtypes[i] for i in range(nbranchvars)]
free(boundtypes)
free(branchbounds)
free(branchvars)
return py_variables, py_branchbounds, py_boundtypes
def getNDomchg(self):
"""Retrieve the number of bound changes due to branching, constraint propagation, and propagation."""
cdef int nbranchings
cdef int nconsprop
cdef int nprop
SCIPnodeGetNDomchg(self.scip_node, &nbranchings, &nconsprop, &nprop)
return nbranchings, nconsprop, nprop
def getDomchg(self):
"""Retrieve domain changes for this node."""
cdef SCIP_DOMCHG* domchg = SCIPnodeGetDomchg(self.scip_node)
if domchg == NULL:
return None
return DomainChanges.create(domchg)
def __hash__(self):
return hash(<size_t>self.scip_node)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.scip_node == (<Node>other).scip_node)
cdef class Variable(Expr):
"""Is a linear expression and has SCIP_VAR*"""
@staticmethod
cdef create(SCIP_VAR* scipvar):
if scipvar == NULL:
raise Warning("cannot create Variable with SCIP_VAR* == NULL")
var = Variable()
var.scip_var = scipvar
Expr.__init__(var, {Term(var) : 1.0})
return var
property name:
def __get__(self):
cname = bytes( SCIPvarGetName(self.scip_var) )
return cname.decode('utf-8')
def ptr(self):
""" """
return <size_t>(self.scip_var)
def __repr__(self):
return self.name
def vtype(self):
"""Retrieve the variables type (BINARY, INTEGER, IMPLINT or CONTINUOUS)"""
vartype = SCIPvarGetType(self.scip_var)
if vartype == SCIP_VARTYPE_BINARY:
return "BINARY"
elif vartype == SCIP_VARTYPE_INTEGER:
return "INTEGER"
elif vartype == SCIP_VARTYPE_CONTINUOUS:
return "CONTINUOUS"
elif vartype == SCIP_VARTYPE_IMPLINT:
return "IMPLINT"
def isOriginal(self):
"""Retrieve whether the variable belongs to the original problem"""
return SCIPvarIsOriginal(self.scip_var)
def isInLP(self):
"""Retrieve whether the variable is a COLUMN variable that is member of the current LP"""
return SCIPvarIsInLP(self.scip_var)
def getIndex(self):
"""Retrieve the unique index of the variable."""
return SCIPvarGetIndex(self.scip_var)
def getCol(self):
"""Retrieve column of COLUMN variable"""
cdef SCIP_COL* scip_col
scip_col = SCIPvarGetCol(self.scip_var)
return Column.create(scip_col)
def getLbOriginal(self):
"""Retrieve original lower bound of variable"""
return SCIPvarGetLbOriginal(self.scip_var)
def getUbOriginal(self):
"""Retrieve original upper bound of variable"""
return SCIPvarGetUbOriginal(self.scip_var)
def getLbGlobal(self):
"""Retrieve global lower bound of variable"""
return SCIPvarGetLbGlobal(self.scip_var)
def getUbGlobal(self):
"""Retrieve global upper bound of variable"""
return SCIPvarGetUbGlobal(self.scip_var)
def getLbLocal(self):
"""Retrieve current lower bound of variable"""
return SCIPvarGetLbLocal(self.scip_var)
def getUbLocal(self):
"""Retrieve current upper bound of variable"""
return SCIPvarGetUbLocal(self.scip_var)
def getObj(self):
"""Retrieve current objective value of variable"""
return SCIPvarGetObj(self.scip_var)
def getLPSol(self):
"""Retrieve the current LP solution value of variable"""
return SCIPvarGetLPSol(self.scip_var)
cdef class Constraint:
"""Base class holding a pointer to corresponding SCIP_CONS"""
@staticmethod
cdef create(SCIP_CONS* scipcons):
if scipcons == NULL:
raise Warning("cannot create Constraint with SCIP_CONS* == NULL")
cons = Constraint()
cons.scip_cons = scipcons
return cons
property name:
def __get__(self):
cname = bytes( SCIPconsGetName(self.scip_cons) )
return cname.decode('utf-8')
def __repr__(self):
return self.name
def isOriginal(self):
"""Retrieve whether the constraint belongs to the original problem"""
return SCIPconsIsOriginal(self.scip_cons)
def isInitial(self):
"""Retrieve True if the relaxation of the constraint should be in the initial LP"""
return SCIPconsIsInitial(self.scip_cons)
def isSeparated(self):
"""Retrieve True if constraint should be separated during LP processing"""
return SCIPconsIsSeparated(self.scip_cons)
def isEnforced(self):
"""Retrieve True if constraint should be enforced during node processing"""
return SCIPconsIsEnforced(self.scip_cons)
def isChecked(self):
"""Retrieve True if constraint should be checked for feasibility"""
return SCIPconsIsChecked(self.scip_cons)
def isPropagated(self):
"""Retrieve True if constraint should be propagated during node processing"""
return SCIPconsIsPropagated(self.scip_cons)
def isLocal(self):
"""Retrieve True if constraint is only locally valid or not added to any (sub)problem"""
return SCIPconsIsLocal(self.scip_cons)
def isModifiable(self):
"""Retrieve True if constraint is modifiable (subject to column generation)"""
return SCIPconsIsModifiable(self.scip_cons)
def isDynamic(self):
"""Retrieve True if constraint is subject to aging"""
return SCIPconsIsDynamic(self.scip_cons)
def isRemovable(self):
"""Retrieve True if constraint's relaxation should be removed from the LP due to aging or cleanup"""
return SCIPconsIsRemovable(self.scip_cons)
def isStickingAtNode(self):
"""Retrieve True if constraint is only locally valid or not added to any (sub)problem"""
return SCIPconsIsStickingAtNode(self.scip_cons)
def isActive(self):
"""returns True iff constraint is active in the current node"""
return SCIPconsIsActive(self.scip_cons)
def isLinear(self):
"""Retrieve True if constraint is linear"""
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
return constype == 'linear'
def isNonlinear(self):
"""Retrieve True if constraint is nonlinear"""
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
return constype == 'nonlinear'
def getConshdlrName(self):
"""Return the constraint handler's name"""
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
return constype
def __hash__(self):
return hash(<size_t>self.scip_cons)
def __eq__(self, other):
return (self.__class__ == other.__class__
and self.scip_cons == (<Constraint>other).scip_cons)
cdef void relayMessage(SCIP_MESSAGEHDLR *messagehdlr, FILE *file, const char *msg) noexcept:
sys.stdout.write(msg.decode('UTF-8'))
cdef void relayErrorMessage(void *messagehdlr, FILE *file, const char *msg) noexcept:
sys.stderr.write(msg.decode('UTF-8'))
# - remove create(), includeDefaultPlugins(), createProbBasic() methods
# - replace free() by "destructor"
# - interface SCIPfreeProb()
##
#@anchor Model
##
cdef class Model:
"""Main class holding a pointer to SCIP for managing most interactions"""
def __init__(self, problemName='model', defaultPlugins=True, Model sourceModel=None, origcopy=False, globalcopy=True, enablepricing=False, createscip=True, threadsafe=False):
"""
:param problemName: name of the problem (default 'model')
:param defaultPlugins: use default plugins? (default True)
:param sourceModel: create a copy of the given Model instance (default None)
:param origcopy: whether to call copy or copyOrig (default False)
:param globalcopy: whether to create a global or a local copy (default True)