0
# Plugin Framework
1
2
Extensible plugin system for implementing custom optimization algorithms and components. PySCIPOpt's plugin framework allows users to extend SCIP's functionality with custom branching rules, constraint handlers, heuristics, cutting plane separators, and other algorithmic components.
3
4
## Capabilities
5
6
### Benders Decomposition
7
8
Custom Benders decomposition handlers for implementing decomposition algorithms and cut generation methods.
9
10
```python { .api }
11
class Benders:
12
"""
13
Benders decomposition handler for implementing custom decomposition methods.
14
15
Benders decomposition separates optimization problems into master and
16
subproblems, iteratively adding cuts to improve the master problem bounds.
17
"""
18
19
def bendersinit(self, benders):
20
"""
21
Initialize Benders decomposition.
22
23
Parameters:
24
- benders: Benders decomposition object
25
"""
26
27
def bendersexit(self, benders):
28
"""
29
Cleanup Benders decomposition.
30
31
Parameters:
32
- benders: Benders decomposition object
33
"""
34
35
def bendersexec(self, benders, result):
36
"""
37
Execute Benders decomposition algorithm.
38
39
Parameters:
40
- benders: Benders decomposition object
41
- result: Dictionary to store result status
42
43
Returns:
44
Should set result["result"] to appropriate SCIP_RESULT value
45
"""
46
```
47
48
### Benders Cut Generation
49
50
Custom Benders cut handlers for generating problem-specific cutting planes in decomposition methods.
51
52
```python { .api }
53
class Benderscut:
54
"""
55
Benders cut handler for custom cut generation in decomposition methods.
56
57
Generates cuts based on dual information from Benders subproblems
58
to strengthen the master problem formulation.
59
"""
60
61
def benderscutexec(self, benderscut, result):
62
"""
63
Execute Benders cut generation.
64
65
Parameters:
66
- benderscut: Benders cut object
67
- result: Dictionary to store result and cut information
68
69
Returns:
70
Should set result["result"] and add cuts if generated
71
"""
72
```
73
74
### Branching Rules
75
76
Custom branching strategies for controlling the branch-and-bound tree exploration.
77
78
```python { .api }
79
class Branchrule:
80
"""
81
Custom branching rule handler for implementing domain-specific
82
branching strategies in branch-and-bound algorithms.
83
"""
84
85
def branchinit(self, branchrule):
86
"""
87
Initialize branching rule.
88
89
Parameters:
90
- branchrule: Branching rule object
91
"""
92
93
def branchexit(self, branchrule):
94
"""
95
Cleanup branching rule.
96
97
Parameters:
98
- branchrule: Branching rule object
99
"""
100
101
def branchexeclp(self, branchrule, allowaddcons, result):
102
"""
103
Execute branching on LP solution.
104
105
Parameters:
106
- branchrule: Branching rule object
107
- allowaddcons (bool): Whether adding constraints is allowed
108
- result: Dictionary to store branching result
109
110
Returns:
111
Should set result["result"] and create child nodes if branching
112
"""
113
114
def branchexecext(self, branchrule, allowaddcons, result):
115
"""
116
Execute branching on external candidates.
117
118
Parameters:
119
- branchrule: Branching rule object
120
- allowaddcons (bool): Whether adding constraints is allowed
121
- result: Dictionary to store branching result
122
"""
123
124
def branchexecps(self, branchrule, allowaddcons, result):
125
"""
126
Execute branching on pseudo solution.
127
128
Parameters:
129
- branchrule: Branching rule object
130
- allowaddcons (bool): Whether adding constraints is allowed
131
- result: Dictionary to store branching result
132
"""
133
```
134
135
### Node Selection
136
137
Custom node selection strategies for controlling the order of node processing in the branch-and-bound tree.
138
139
```python { .api }
140
class Nodesel:
141
"""
142
Node selection handler for implementing custom strategies
143
to choose which node to process next in branch-and-bound.
144
"""
145
146
def nodeselinit(self, nodesel):
147
"""
148
Initialize node selector.
149
150
Parameters:
151
- nodesel: Node selector object
152
"""
153
154
def nodeselexit(self, nodesel):
155
"""
156
Cleanup node selector.
157
158
Parameters:
159
- nodesel: Node selector object
160
"""
161
162
def nodeselselect(self, nodesel, selnode, result):
163
"""
164
Select next node to process.
165
166
Parameters:
167
- nodesel: Node selector object
168
- selnode: List to store selected node
169
- result: Dictionary to store selection result
170
171
Should select the most promising node from the tree
172
"""
173
174
def nodeselcomp(self, nodesel, node1, node2):
175
"""
176
Compare two nodes for selection priority.
177
178
Parameters:
179
- nodesel: Node selector object
180
- node1: First node to compare
181
- node2: Second node to compare
182
183
Returns:
184
int: -1 if node1 has higher priority, 1 if node2, 0 if equal
185
"""
186
```
187
188
### Constraint Handlers
189
190
Custom constraint types with propagation, separation, and enforcement methods.
191
192
```python { .api }
193
class Conshdlr:
194
"""
195
Constraint handler for implementing custom constraint types
196
with specialized propagation and separation algorithms.
197
"""
198
199
def consinit(self, constraints):
200
"""
201
Initialize constraint handler.
202
203
Parameters:
204
- constraints: List of constraints handled by this handler
205
"""
206
207
def consexit(self, constraints):
208
"""
209
Cleanup constraint handler.
210
211
Parameters:
212
- constraints: List of constraints
213
"""
214
215
def constrans(self, sourceconstraints, targetconstraints):
216
"""
217
Transform constraints during problem transformation.
218
219
Parameters:
220
- sourceconstraints: Original constraints
221
- targetconstraints: Transformed constraints
222
"""
223
224
def consenfolp(self, constraints, nusefulconss, solinfeasible, result):
225
"""
226
Enforce constraints for LP solution.
227
228
Parameters:
229
- constraints: List of constraints to enforce
230
- nusefulconss (int): Number of useful constraints
231
- solinfeasible (bool): Whether solution is already known infeasible
232
- result: Dictionary to store enforcement result
233
"""
234
235
def consenfops(self, constraints, nusefulconss, solinfeasible, objinfeasible, result):
236
"""
237
Enforce constraints for pseudo solution.
238
239
Parameters:
240
- constraints: List of constraints to enforce
241
- nusefulconss (int): Number of useful constraints
242
- solinfeasible (bool): Whether solution is already known infeasible
243
- objinfeasible (bool): Whether solution is objectively infeasible
244
- result: Dictionary to store enforcement result
245
"""
246
247
def conscheck(self, constraints, solution, checkintegrality, checklprows, printreason, result):
248
"""
249
Check feasibility of solution.
250
251
Parameters:
252
- constraints: List of constraints to check
253
- solution: Solution to check
254
- checkintegrality (bool): Check integrality constraints
255
- checklprows (bool): Check LP row constraints
256
- printreason (bool): Print infeasibility reasons
257
- result: Dictionary to store check result
258
"""
259
260
def consprop(self, constraints, nusefulconss, result):
261
"""
262
Propagate constraints (domain reduction).
263
264
Parameters:
265
- constraints: List of constraints to propagate
266
- nusefulconss (int): Number of useful constraints
267
- result: Dictionary to store propagation result
268
"""
269
270
def conssepalp(self, constraints, nusefulconss, result):
271
"""
272
Separate constraints for LP solution.
273
274
Parameters:
275
- constraints: List of constraints for separation
276
- nusefulconss (int): Number of useful constraints
277
- result: Dictionary to store separation result
278
"""
279
280
def conssepasol(self, constraints, nusefulconss, solution, result):
281
"""
282
Separate constraints for given solution.
283
284
Parameters:
285
- constraints: List of constraints for separation
286
- nusefulconss (int): Number of useful constraints
287
- solution: Solution to separate
288
- result: Dictionary to store separation result
289
"""
290
```
291
292
### Event Handlers
293
294
Event handling system for monitoring solver progress and implementing custom callbacks.
295
296
```python { .api }
297
class Eventhdlr:
298
"""
299
Event handler for monitoring solver events and implementing
300
custom callbacks during the optimization process.
301
"""
302
303
def eventinit(self, eventhdlr):
304
"""
305
Initialize event handler.
306
307
Parameters:
308
- eventhdlr: Event handler object
309
"""
310
311
def eventexit(self, eventhdlr):
312
"""
313
Cleanup event handler.
314
315
Parameters:
316
- eventhdlr: Event handler object
317
"""
318
319
def eventexec(self, eventhdlr, event):
320
"""
321
Execute event handling.
322
323
Parameters:
324
- eventhdlr: Event handler object
325
- event: Event object containing event information
326
327
Event types include:
328
- Variable events: bound changes, domain changes, addition/deletion
329
- Node events: focus, unfocus, branch, solution found
330
- LP events: solved, objective change
331
- Solution events: found, improved
332
"""
333
```
334
335
### Primal Heuristics
336
337
Custom heuristic algorithms for finding feasible solutions.
338
339
```python { .api }
340
class Heur:
341
"""
342
Primal heuristic handler for implementing custom algorithms
343
to find feasible solutions during the optimization process.
344
"""
345
346
def heurinit(self, heur):
347
"""
348
Initialize heuristic.
349
350
Parameters:
351
- heur: Heuristic object
352
"""
353
354
def heurexit(self, heur):
355
"""
356
Cleanup heuristic.
357
358
Parameters:
359
- heur: Heuristic object
360
"""
361
362
def heurexec(self, heur, heurtiming, nodeinfeasible, result):
363
"""
364
Execute heuristic algorithm.
365
366
Parameters:
367
- heur: Heuristic object
368
- heurtiming: Timing information (when heuristic is called)
369
- nodeinfeasible (bool): Whether current node is infeasible
370
- result: Dictionary to store heuristic result
371
372
Timing values:
373
- BEFORENODE: Before processing node
374
- DURINGLPLOOP: During LP solving loop
375
- AFTERLPLOOP: After LP solving loop
376
- AFTERNODE: After processing node
377
"""
378
```
379
380
### Presolving Methods
381
382
Custom presolving routines for problem simplification and preprocessing.
383
384
```python { .api }
385
class Presol:
386
"""
387
Presolving handler for implementing custom problem simplification
388
and preprocessing routines before the main optimization begins.
389
"""
390
391
def presolinit(self, presol):
392
"""
393
Initialize presolving method.
394
395
Parameters:
396
- presol: Presolving object
397
"""
398
399
def presolexit(self, presol):
400
"""
401
Cleanup presolving method.
402
403
Parameters:
404
- presol: Presolving object
405
"""
406
407
def presolexec(self, presol, presolvinground, presoltiming, nnewfixedvars,
408
nnewaggrvars, nnewchgvartypes, nnewchgbds, nnewholes,
409
nnewdelconss, nnewaddconss, nnewupgdconss, nnewchgcoefs,
410
nnewchgsides, nfixedvars, naggrvars, nchgvartypes,
411
nchgbds, naddholes, ndelconss, naddconss, nupgdconss,
412
nchgcoefs, nchgsides, result):
413
"""
414
Execute presolving routine.
415
416
Parameters:
417
- presol: Presolving object
418
- presolvinground (int): Current presolving round
419
- presoltiming: Timing information
420
- Various counters for tracking changes made
421
- result: Dictionary to store presolving result
422
423
Should modify problem and update counters for changes made
424
"""
425
```
426
427
### Variable Pricers
428
429
Column generation algorithms for adding variables dynamically during optimization.
430
431
```python { .api }
432
class Pricer:
433
"""
434
Variable pricer for implementing column generation algorithms
435
that add variables dynamically during the optimization process.
436
"""
437
438
def pricerinit(self, pricer):
439
"""
440
Initialize pricer.
441
442
Parameters:
443
- pricer: Pricer object
444
"""
445
446
def pricerexit(self, pricer):
447
"""
448
Cleanup pricer.
449
450
Parameters:
451
- pricer: Pricer object
452
"""
453
454
def pricerredcost(self, pricer, result):
455
"""
456
Perform reduced cost pricing.
457
458
Parameters:
459
- pricer: Pricer object
460
- result: Dictionary to store pricing result
461
462
Should add variables with negative reduced cost
463
"""
464
465
def pricerfarkas(self, pricer, result):
466
"""
467
Perform Farkas pricing for infeasible LP.
468
469
Parameters:
470
- pricer: Pricer object
471
- result: Dictionary to store pricing result
472
473
Should add variables to prove infeasibility
474
"""
475
```
476
477
### Propagators
478
479
Custom domain propagation algorithms for tightening variable bounds.
480
481
```python { .api }
482
class Prop:
483
"""
484
Propagator handler for implementing custom domain propagation
485
algorithms that tighten variable bounds and domains.
486
"""
487
488
def propinit(self, prop):
489
"""
490
Initialize propagator.
491
492
Parameters:
493
- prop: Propagator object
494
"""
495
496
def propexit(self, prop):
497
"""
498
Cleanup propagator.
499
500
Parameters:
501
- prop: Propagator object
502
"""
503
504
def propexec(self, prop, proptiming, result):
505
"""
506
Execute propagation algorithm.
507
508
Parameters:
509
- prop: Propagator object
510
- proptiming: When propagation is called
511
- result: Dictionary to store propagation result
512
513
Timing values:
514
- BEFORELP: Before LP solving
515
- DURINGLPLOOP: During LP solving
516
- AFTERLPLOOP: After LP solving
517
"""
518
519
def propresprop(self, prop, cutoff, result):
520
"""
521
Resolve propagation after bound changes.
522
523
Parameters:
524
- prop: Propagator object
525
- cutoff (bool): Whether cutoff occurred
526
- result: Dictionary to store result
527
"""
528
```
529
530
### Cutting Plane Separators
531
532
Custom cutting plane generation for strengthening LP relaxations.
533
534
```python { .api }
535
class Sepa:
536
"""
537
Separator handler for implementing custom cutting plane generation
538
algorithms to strengthen LP relaxations.
539
"""
540
541
def sepainit(self, sepa):
542
"""
543
Initialize separator.
544
545
Parameters:
546
- sepa: Separator object
547
"""
548
549
def sepaexit(self, sepa):
550
"""
551
Cleanup separator.
552
553
Parameters:
554
- sepa: Separator object
555
"""
556
557
def sepaexeclp(self, sepa, result):
558
"""
559
Execute separation for LP solution.
560
561
Parameters:
562
- sepa: Separator object
563
- result: Dictionary to store separation result
564
565
Should add cutting planes that cut off current LP solution
566
"""
567
568
def sepaexecsol(self, sepa, solution, result):
569
"""
570
Execute separation for given solution.
571
572
Parameters:
573
- sepa: Separator object
574
- solution: Solution to separate
575
- result: Dictionary to store separation result
576
"""
577
```
578
579
### File Readers
580
581
Custom file format readers for specialized problem formats.
582
583
```python { .api }
584
class Reader:
585
"""
586
File reader handler for implementing custom file format parsers
587
to read optimization problems from specialized formats.
588
"""
589
590
def readerread(self, reader, filename, result):
591
"""
592
Read problem from file.
593
594
Parameters:
595
- reader: Reader object
596
- filename (str): Path to file to read
597
- result: Dictionary to store reading result
598
599
Should parse file and create corresponding SCIP problem
600
"""
601
602
def readerwrite(self, reader, file, name, transformed, objsense, objscale,
603
objoffset, binvars, intvars, implvars, contvars, fixedvars,
604
startnvars, conss, maxnconss, startnconss, genericnames, result):
605
"""
606
Write problem to file.
607
608
Parameters:
609
- reader: Reader object
610
- file: File handle for writing
611
- Various problem components to write
612
- result: Dictionary to store writing result
613
"""
614
```
615
616
### Linear Programming Interface
617
618
Access to the LP relaxation and linear programming solver interface for advanced LP operations.
619
620
```python { .api }
621
class LP:
622
"""
623
Linear programming interface providing access to LP relaxation,
624
dual values, basis information, and advanced LP solver operations.
625
"""
626
627
def getNRows(self):
628
"""
629
Get number of rows in current LP.
630
631
Returns:
632
int: Number of LP rows
633
"""
634
635
def getNCols(self):
636
"""
637
Get number of columns in current LP.
638
639
Returns:
640
int: Number of LP columns
641
"""
642
643
def getSolstat(self):
644
"""
645
Get LP solution status.
646
647
Returns:
648
SCIP_LPSOLSTAT: LP solution status
649
"""
650
651
def getObjval(self):
652
"""
653
Get LP objective value.
654
655
Returns:
656
float: LP objective value
657
"""
658
659
def getColsData(self):
660
"""
661
Get LP column data.
662
663
Returns:
664
tuple: Column data including lower bounds, upper bounds, objective coefficients
665
"""
666
667
def getRowsData(self):
668
"""
669
Get LP row data.
670
671
Returns:
672
tuple: Row data including left-hand sides, right-hand sides, row matrix
673
"""
674
675
def getPrimalSol(self):
676
"""
677
Get primal LP solution.
678
679
Returns:
680
list: Primal solution values for LP columns
681
"""
682
683
def getDualSol(self):
684
"""
685
Get dual LP solution.
686
687
Returns:
688
list: Dual solution values for LP rows
689
"""
690
691
def getRedcostSol(self):
692
"""
693
Get reduced cost solution.
694
695
Returns:
696
list: Reduced costs for LP columns
697
"""
698
```
699
700
## Usage Examples
701
702
### Custom Branching Rule
703
704
```python
705
from pyscipopt import Model, Branchrule, SCIP_RESULT
706
707
class CustomBranchingRule(Branchrule):
708
"""Example: Most fractional branching rule"""
709
710
def branchexeclp(self, branchrule, allowaddcons, result):
711
"""Branch on most fractional variable"""
712
713
# Get LP solution
714
lpsol = {}
715
for var in self.model.getVars():
716
if var.vtype() in ['B', 'I']: # Binary or integer variables
717
lpsol[var] = self.model.getVal(var)
718
719
# Find most fractional variable
720
most_fractional_var = None
721
max_fractionality = 0
722
723
for var, val in lpsol.items():
724
fractionality = min(val - int(val), int(val) + 1 - val)
725
if fractionality > max_fractionality:
726
max_fractionality = fractionality
727
most_fractional_var = var
728
729
if most_fractional_var is not None and max_fractionality > 1e-6:
730
# Create two child nodes
731
val = lpsol[most_fractional_var]
732
733
# Create down branch: var <= floor(val)
734
down_node = self.model.createChild(1.0, 1.0) # priority, estimate
735
self.model.addConsNode(down_node,
736
most_fractional_var <= int(val))
737
738
# Create up branch: var >= ceil(val)
739
up_node = self.model.createChild(1.0, 1.0)
740
self.model.addConsNode(up_node,
741
most_fractional_var >= int(val) + 1)
742
743
result["result"] = SCIP_RESULT.BRANCHED
744
else:
745
result["result"] = SCIP_RESULT.DIDNOTRUN
746
747
# Usage
748
model = Model("branching_example")
749
750
# Add variables and constraints
751
x = model.addVar(name="x", vtype="I", lb=0, ub=10)
752
y = model.addVar(name="y", vtype="I", lb=0, ub=8)
753
754
model.addCons(2.5*x + 3.7*y <= 15.3)
755
model.addCons(1.2*x + 0.8*y >= 3.1)
756
757
model.setObjective(x + y, "maximize")
758
759
# Include custom branching rule
760
branching_rule = CustomBranchingRule()
761
model.includeBranchrule(branching_rule, "MostFractional", "Branch on most fractional variable",
762
priority=100000, maxdepth=-1, maxbounddist=1.0)
763
764
model.optimize()
765
```
766
767
### Custom Heuristic
768
769
```python
770
from pyscipopt import Model, Heur, SCIP_RESULT, SCIP_HEURTIMING
771
772
class RoundingHeuristic(Heur):
773
"""Simple rounding heuristic for mixed-integer problems"""
774
775
def heurexec(self, heur, heurtiming, nodeinfeasible, result):
776
"""Execute rounding heuristic"""
777
778
if nodeinfeasible:
779
result["result"] = SCIP_RESULT.DIDNOTRUN
780
return
781
782
# Get current LP solution
783
lpsol = {}
784
for var in self.model.getVars():
785
lpsol[var] = self.model.getVal(var)
786
787
# Create rounded solution
788
rounded_sol = self.model.createSol(heur)
789
790
for var in self.model.getVars():
791
if var.vtype() in ['B', 'I']:
792
# Round to nearest integer
793
rounded_val = round(lpsol[var])
794
# Respect bounds
795
rounded_val = max(var.getLbLocal(), min(var.getUbLocal(), rounded_val))
796
self.model.setSolVal(rounded_sol, var, rounded_val)
797
else:
798
# Keep continuous variables as is
799
self.model.setSolVal(rounded_sol, var, lpsol[var])
800
801
# Try to add solution
802
feasible = self.model.checkSol(rounded_sol)
803
if feasible:
804
self.model.addSol(rounded_sol)
805
result["result"] = SCIP_RESULT.FOUNDSOL
806
else:
807
result["result"] = SCIP_RESULT.DIDNOTFIND
808
809
# Free solution
810
self.model.freeSol(rounded_sol)
811
812
# Usage
813
model = Model("heuristic_example")
814
815
# Create mixed-integer problem
816
x = [model.addVar(name=f"x_{i}", vtype="I", lb=0, ub=10) for i in range(5)]
817
y = [model.addVar(name=f"y_{i}", lb=0, ub=5) for i in range(3)]
818
819
# Add constraints
820
model.addCons(quicksum(2*x[i] for i in range(5)) + quicksum(y[j] for j in range(3)) <= 25)
821
model.addCons(quicksum(x[i] for i in range(5)) >= 3)
822
823
# Objective
824
model.setObjective(quicksum(x[i] for i in range(5)) + quicksum(3*y[j] for j in range(3)), "maximize")
825
826
# Include custom heuristic
827
rounding_heur = RoundingHeuristic()
828
model.includeHeur(rounding_heur, "SimpleRounding", "Round fractional values to nearest integer",
829
'L', priority=1000, freq=1, freqofs=0, maxdepth=-1,
830
timingmask=SCIP_HEURTIMING.AFTERLPLOOP)
831
832
model.optimize()
833
```
834
835
### Custom Constraint Handler
836
837
```python
838
from pyscipopt import Model, Conshdlr, SCIP_RESULT
839
840
class AllDifferentConstraintHandler(Conshdlr):
841
"""All-different constraint: all variables must have different values"""
842
843
def conscheck(self, constraints, solution, checkintegrality, checklprows, printreason, result):
844
"""Check if all variables have different values"""
845
846
for cons in constraints:
847
# Get variables from constraint (stored during constraint creation)
848
variables = cons.data["variables"]
849
850
# Get solution values
851
values = [self.model.getSolVal(solution, var) for var in variables]
852
853
# Check if all values are different (within tolerance)
854
epsilon = 1e-6
855
for i in range(len(values)):
856
for j in range(i+1, len(values)):
857
if abs(values[i] - values[j]) < epsilon:
858
if printreason:
859
print(f"All-different violated: {variables[i].name} = {variables[j].name} = {values[i]}")
860
result["result"] = SCIP_RESULT.INFEASIBLE
861
return
862
863
result["result"] = SCIP_RESULT.FEASIBLE
864
865
def consenfolp(self, constraints, nusefulconss, solinfeasible, result):
866
"""Enforce all-different constraints for LP solution"""
867
868
cuts_added = False
869
870
for cons in constraints:
871
variables = cons.data["variables"]
872
873
# Get current LP values
874
values = [(var, self.model.getVal(var)) for var in variables]
875
876
# Find pairs of variables with same values
877
epsilon = 1e-6
878
for i in range(len(values)):
879
for j in range(i+1, len(values)):
880
var1, val1 = values[i]
881
var2, val2 = values[j]
882
883
if abs(val1 - val2) < epsilon:
884
# Add cut to separate this solution
885
# For integer variables: var1 - var2 >= 1 OR var2 - var1 >= 1
886
# Relaxed: |var1 - var2| >= 1
887
888
# Add two cuts
889
cut1_expr = var1 - var2 >= 1
890
cut2_expr = var2 - var1 >= 1
891
892
self.model.addCons(cut1_expr, name=f"alldiff_cut1_{var1.name}_{var2.name}")
893
self.model.addCons(cut2_expr, name=f"alldiff_cut2_{var1.name}_{var2.name}")
894
895
cuts_added = True
896
897
if cuts_added:
898
result["result"] = SCIP_RESULT.SEPARATED
899
else:
900
result["result"] = SCIP_RESULT.FEASIBLE
901
902
# Usage
903
model = Model("alldiff_example")
904
905
# Create variables
906
n = 4
907
x = [model.addVar(name=f"x_{i}", vtype="I", lb=1, ub=n) for i in range(n)]
908
909
# Create all-different constraint handler
910
alldiff_handler = AllDifferentConstraintHandler()
911
model.includeConshdlr(alldiff_handler, "AllDifferent", "All variables must be different",
912
sepapriority=100, enfopriority=100, checkpriority=-100,
913
sepafreq=1, propfreq=1, eagerfreq=100, maxprerounds=-1,
914
delaysepa=False, delayprop=False, needscons=True)
915
916
# Add all-different constraint
917
alldiff_cons = model.createCons(alldiff_handler, "alldiff_constraint")
918
alldiff_cons.data = {"variables": x}
919
model.addCons(alldiff_cons)
920
921
# Additional constraints
922
model.addCons(quicksum(x[i] for i in range(n)) == 10)
923
924
# Objective
925
model.setObjective(quicksum((i+1)*x[i] for i in range(n)), "maximize")
926
927
model.optimize()
928
```