0
# Core Modeling Classes
1
2
Fundamental classes for creating and manipulating linear and mixed integer programming problems in PuLP. These classes provide the foundation for mathematical optimization modeling with intuitive Python syntax.
3
4
## Capabilities
5
6
### Problem Management
7
8
The LpProblem class serves as the main container for optimization problems, managing variables, constraints, objectives, and solution processes.
9
10
```python { .api }
11
class LpProblem:
12
def __init__(self, name="NoName", sense=LpMinimize):
13
"""
14
Create a new optimization problem.
15
16
Parameters:
17
- name (str): Problem name for identification
18
- sense (int): Optimization direction (LpMinimize=1 or LpMaximize=-1)
19
"""
20
21
def solve(self, solver=None, **kwargs):
22
"""
23
Solve the optimization problem.
24
25
Parameters:
26
- solver: Solver instance (defaults to LpSolverDefault)
27
- **kwargs: Additional solver-specific parameters
28
29
Returns:
30
int: Status code (LpStatusOptimal=1, LpStatusInfeasible=-1, etc.)
31
"""
32
33
def writeLP(self, filename):
34
"""
35
Export problem to LP format file.
36
37
Parameters:
38
- filename (str): Output file path
39
"""
40
41
def writeMPS(self, filename):
42
"""
43
Export problem to MPS format file.
44
45
Parameters:
46
- filename (str): Output file path
47
"""
48
49
def copy(self):
50
"""
51
Create a deep copy of the problem.
52
53
Returns:
54
LpProblem: Copy of the problem
55
"""
56
57
def deepcopy(self):
58
"""
59
Create a deep copy of the problem with all components.
60
61
Returns:
62
LpProblem: Deep copy of the problem
63
"""
64
65
def variables(self):
66
"""
67
Get all variables in the problem.
68
69
Returns:
70
list: List of LpVariable objects
71
"""
72
73
@property
74
def objective(self):
75
"""Get or set the objective function (LpAffineExpression)."""
76
77
@property
78
def constraints(self):
79
"""Get dictionary of all constraints in the problem."""
80
81
@property
82
def status(self):
83
"""Get the solution status of the problem."""
84
85
def sequentialSolve(self, objectives):
86
"""
87
Solve problem with multiple objectives sequentially.
88
89
Parameters:
90
- objectives (list): List of LpAffineExpression objectives to optimize in order
91
92
Returns:
93
int: Status code of final solve
94
"""
95
96
def toJson(self, filename):
97
"""
98
Export problem to JSON format file.
99
100
Parameters:
101
- filename (str): Output JSON file path
102
"""
103
104
def fromJson(self, filename):
105
"""
106
Load problem from JSON format file.
107
108
Parameters:
109
- filename (str): Input JSON file path
110
"""
111
112
def roundSolution(self, epsilon=1e-7):
113
"""
114
Round variable values in solution to remove numerical precision artifacts.
115
116
Parameters:
117
- epsilon (float): Tolerance for rounding near-integer values
118
"""
119
120
def checkDuplicateVars(self):
121
"""
122
Check for duplicate variable names in the problem.
123
124
Returns:
125
bool: True if duplicates found, False otherwise
126
"""
127
128
def numVariables(self):
129
"""
130
Get the number of variables in the problem.
131
132
Returns:
133
int: Number of variables
134
"""
135
136
def numConstraints(self):
137
"""
138
Get the number of constraints in the problem.
139
140
Returns:
141
int: Number of constraints
142
"""
143
144
def isMIP(self):
145
"""
146
Check if problem is a Mixed Integer Program.
147
148
Returns:
149
bool: True if problem contains integer or binary variables
150
"""
151
152
def valid(self):
153
"""
154
Check if problem is valid and well-formed.
155
156
Returns:
157
bool: True if problem is valid
158
"""
159
160
def assignStatus(self, status):
161
"""
162
Assign solution status to the problem.
163
164
Parameters:
165
- status (int): Status code to assign
166
"""
167
168
def toDict(self):
169
"""
170
Convert problem to dictionary representation.
171
172
Returns:
173
dict: Dictionary representation of the problem
174
"""
175
176
def fromDict(self, data):
177
"""
178
Load problem from dictionary representation.
179
180
Parameters:
181
- data (dict): Dictionary containing problem data
182
"""
183
184
def toDataclass(self):
185
"""
186
Convert problem to dataclass representation.
187
188
Returns:
189
Dataclass representation of the problem
190
"""
191
192
def fromDataclass(self, data):
193
"""
194
Load problem from dataclass representation.
195
196
Parameters:
197
- data: Dataclass containing problem data
198
"""
199
200
def variablesDict(self):
201
"""
202
Get dictionary of all variables keyed by name.
203
204
Returns:
205
dict: Dictionary mapping variable names to LpVariable objects
206
"""
207
208
def addVariable(self, var):
209
"""
210
Add a single variable to the problem.
211
212
Parameters:
213
- var (LpVariable): Variable to add
214
"""
215
216
def addVariables(self, vars):
217
"""
218
Add multiple variables to the problem.
219
220
Parameters:
221
- vars (list): List of LpVariable objects to add
222
"""
223
224
def addConstraint(self, constraint):
225
"""
226
Add a constraint to the problem.
227
228
Parameters:
229
- constraint (LpConstraint): Constraint to add
230
"""
231
232
def extend(self, other):
233
"""
234
Extend problem with variables and constraints from another problem.
235
236
Parameters:
237
- other (LpProblem): Problem to merge into this problem
238
"""
239
240
def setObjective(self, obj):
241
"""
242
Set the objective function for the problem.
243
244
Parameters:
245
- obj (LpAffineExpression): Objective expression to minimize/maximize
246
"""
247
248
def resolve(self):
249
"""
250
Re-solve the problem with the same solver and parameters.
251
252
Returns:
253
int: Status code
254
"""
255
256
def getSense(self):
257
"""
258
Get the optimization sense (minimize or maximize).
259
260
Returns:
261
int: LpMinimize (1) or LpMaximize (-1)
262
"""
263
```
264
265
Usage example:
266
267
```python
268
# Create minimization problem
269
prob = LpProblem("Production_Planning", LpMinimize)
270
271
# Add variables and constraints
272
x = LpVariable("production", 0, 1000)
273
prob += 2*x >= 100 # Minimum production constraint
274
prob += 3*x # Objective: minimize cost
275
276
# Solve
277
status = prob.solve()
278
print(f"Status: {LpStatus[status]}")
279
```
280
281
### Variable Creation and Management
282
283
The LpVariable class represents decision variables in optimization problems with support for bounds, categories, and bulk creation methods.
284
285
```python { .api }
286
class LpVariable:
287
def __init__(self, name, lowBound=None, upBound=None, cat=LpContinuous, e=None):
288
"""
289
Create a decision variable.
290
291
Parameters:
292
- name (str): Variable name
293
- lowBound (float, optional): Lower bound (default: None = -infinity)
294
- upBound (float, optional): Upper bound (default: None = +infinity)
295
- cat (str): Variable category (LpContinuous, LpInteger, LpBinary)
296
- e (LpElement, optional): Associated element for column generation
297
"""
298
299
@classmethod
300
def dicts(cls, name, indices, lowBound=None, upBound=None, cat=LpContinuous):
301
"""
302
Create dictionary of variables with shared properties.
303
304
Parameters:
305
- name (str): Base name for variables
306
- indices: Iterable of indices for variable names
307
- lowBound (float, optional): Lower bound for all variables
308
- upBound (float, optional): Upper bound for all variables
309
- cat (str): Variable category for all variables
310
311
Returns:
312
dict: Dictionary mapping indices to LpVariable objects
313
"""
314
315
@classmethod
316
def dict(cls, name, indices, lowBound=None, upBound=None, cat=LpContinuous):
317
"""
318
Alias for dicts() method.
319
"""
320
321
@classmethod
322
def matrix(cls, name, indices1, indices2, lowBound=None, upBound=None, cat=LpContinuous):
323
"""
324
Create 2D matrix of variables.
325
326
Parameters:
327
- name (str): Base name for variables
328
- indices1: First dimension indices
329
- indices2: Second dimension indices
330
- lowBound (float, optional): Lower bound for all variables
331
- upBound (float, optional): Upper bound for all variables
332
- cat (str): Variable category for all variables
333
334
Returns:
335
dict: Nested dictionary of LpVariable objects
336
"""
337
338
def bounds(self, low, up):
339
"""
340
Set variable bounds.
341
342
Parameters:
343
- low (float): Lower bound
344
- up (float): Upper bound
345
"""
346
347
def positive(self):
348
"""
349
Make variable non-negative (set lower bound to 0).
350
"""
351
352
def value(self):
353
"""
354
Get the solution value of the variable.
355
356
Returns:
357
float: Variable value in solution (None if not solved)
358
"""
359
360
def setInitialValue(self, val):
361
"""
362
Set initial value for warm starts.
363
364
Parameters:
365
- val (float): Initial value
366
"""
367
368
@property
369
def lowBound(self):
370
"""Get or set the lower bound of the variable."""
371
372
@property
373
def upBound(self):
374
"""Get or set the upper bound of the variable."""
375
376
@property
377
def cat(self):
378
"""Get or set the category of the variable."""
379
380
@property
381
def name(self):
382
"""Get the name of the variable."""
383
384
def roundedValue(self, epsilon=1e-7):
385
"""
386
Get rounded solution value of the variable.
387
388
Parameters:
389
- epsilon (float): Tolerance for rounding near-integer values
390
391
Returns:
392
float: Rounded variable value
393
"""
394
395
def valueOrDefault(self):
396
"""
397
Get variable value or default within bounds if not solved.
398
399
Returns:
400
float: Variable value or default within bounds
401
"""
402
403
def fixValue(self, value=None):
404
"""
405
Fix variable to a specific value.
406
407
Parameters:
408
- value (float, optional): Value to fix variable to (uses current value if None)
409
"""
410
411
def unfixValue(self):
412
"""
413
Unfix variable, restoring original bounds.
414
"""
415
416
def isFixed(self):
417
"""
418
Check if variable is fixed to a specific value.
419
420
Returns:
421
bool: True if variable is fixed
422
"""
423
424
def isBinary(self):
425
"""
426
Check if variable is binary (0 or 1).
427
428
Returns:
429
bool: True if variable category is LpBinary
430
"""
431
432
def isInteger(self):
433
"""
434
Check if variable is integer.
435
436
Returns:
437
bool: True if variable category is LpInteger or LpBinary
438
"""
439
440
def isFree(self):
441
"""
442
Check if variable is free (unbounded).
443
444
Returns:
445
bool: True if variable has no bounds
446
"""
447
448
def isConstant(self):
449
"""
450
Check if variable has constant value (both bounds equal).
451
452
Returns:
453
bool: True if lower bound equals upper bound
454
"""
455
456
def isPositive(self):
457
"""
458
Check if variable is constrained to be positive.
459
460
Returns:
461
bool: True if lower bound >= 0
462
"""
463
464
def getLb(self):
465
"""
466
Get lower bound of the variable.
467
468
Returns:
469
float: Lower bound value
470
"""
471
472
def getUb(self):
473
"""
474
Get upper bound of the variable.
475
476
Returns:
477
float: Upper bound value
478
"""
479
480
def valid(self, eps=1e-7):
481
"""
482
Check if variable value is within bounds.
483
484
Parameters:
485
- eps (float): Tolerance for bound checking
486
487
Returns:
488
bool: True if variable value is valid
489
"""
490
491
def infeasibilityGap(self):
492
"""
493
Calculate infeasibility gap for variable bounds.
494
495
Returns:
496
float: Gap value if infeasible, 0 if feasible
497
"""
498
499
def toDict(self):
500
"""
501
Convert variable to dictionary representation.
502
503
Returns:
504
dict: Dictionary representation of the variable
505
"""
506
507
def fromDict(self, data):
508
"""
509
Load variable from dictionary representation.
510
511
Parameters:
512
- data (dict): Dictionary containing variable data
513
"""
514
515
def toDataclass(self):
516
"""
517
Convert variable to dataclass representation.
518
519
Returns:
520
Dataclass representation of the variable
521
"""
522
523
def fromDataclass(self, data):
524
"""
525
Load variable from dataclass representation.
526
527
Parameters:
528
- data: Dataclass containing variable data
529
"""
530
```
531
532
Usage examples:
533
534
```python
535
# Single variables
536
x = LpVariable("x", 0, 10) # 0 <= x <= 10
537
y = LpVariable("y", cat="Binary") # Binary variable
538
z = LpVariable("z", lowBound=0) # Non-negative variable
539
540
# Dictionary of variables
541
plants = ["A", "B", "C"]
542
production = LpVariable.dicts("prod", plants, 0, 1000)
543
# Creates: prod_A, prod_B, prod_C with bounds [0, 1000]
544
545
# Matrix of variables
546
supply_points = ["S1", "S2"]
547
demand_points = ["D1", "D2", "D3"]
548
transport = LpVariable.matrix("transport", supply_points, demand_points, 0)
549
# Creates: transport_S1_D1, transport_S1_D2, etc.
550
```
551
552
### Constraint Management
553
554
The LpConstraint class handles mathematical relationships between variables with support for different constraint senses and elastic subproblems.
555
556
```python { .api }
557
class LpConstraint:
558
def __init__(self, e=None, sense=LpConstraintEQ, name=None, rhs=None):
559
"""
560
Create a constraint.
561
562
Parameters:
563
- e (LpAffineExpression, optional): Left-hand side expression
564
- sense (int): Constraint sense (LpConstraintLE=-1, LpConstraintEQ=0, LpConstraintGE=1)
565
- name (str, optional): Constraint name
566
- rhs (float, optional): Right-hand side value
567
"""
568
569
def changeRHS(self, RHS):
570
"""
571
Change the right-hand side value.
572
573
Parameters:
574
- RHS (float): New right-hand side value
575
"""
576
577
def copy(self):
578
"""
579
Create a copy of the constraint.
580
581
Returns:
582
LpConstraint: Copy of the constraint
583
"""
584
585
def makeElasticSubProblem(self, penalty=1e6, proportionFreeBound=1e-4, proportionFreeBoundList=None):
586
"""
587
Create elastic version of constraint for infeasibility analysis.
588
589
Parameters:
590
- penalty (float): Penalty coefficient for constraint violations
591
- proportionFreeBound (float): Proportion of constraint bound that can be violated freely
592
- proportionFreeBoundList (list, optional): List of proportion bounds for multiple constraints
593
594
Returns:
595
FixedElasticSubProblem: Elastic subproblem object
596
"""
597
598
def emptyCopy(self):
599
"""
600
Create an empty copy of the constraint without coefficients.
601
602
Returns:
603
LpConstraint: Empty constraint with same sense and name
604
"""
605
606
def addInPlace(self, other):
607
"""
608
Add another constraint or expression to this constraint in place.
609
610
Parameters:
611
- other (LpConstraint or LpAffineExpression): Expression to add
612
"""
613
614
def getLb(self):
615
"""
616
Get lower bound equivalent of constraint.
617
618
Returns:
619
float: Lower bound value based on constraint sense
620
"""
621
622
def getUb(self):
623
"""
624
Get upper bound equivalent of constraint.
625
626
Returns:
627
float: Upper bound value based on constraint sense
628
"""
629
630
def valid(self):
631
"""
632
Check if constraint is valid and well-formed.
633
634
Returns:
635
bool: True if constraint is valid
636
"""
637
638
def value(self):
639
"""
640
Calculate the value of the constraint's left-hand side.
641
642
Returns:
643
float: Current value of constraint expression
644
"""
645
646
def keys(self):
647
"""
648
Get variables in the constraint.
649
650
Returns:
651
dict_keys: Variable keys in constraint
652
"""
653
654
def values(self):
655
"""
656
Get coefficients in the constraint.
657
658
Returns:
659
dict_values: Coefficient values in constraint
660
"""
661
662
def items(self):
663
"""
664
Get variable-coefficient pairs in the constraint.
665
666
Returns:
667
dict_items: Variable-coefficient pairs
668
"""
669
670
def get(self, var, default=0):
671
"""
672
Get coefficient for a variable.
673
674
Parameters:
675
- var (LpVariable): Variable to get coefficient for
676
- default (float): Default value if variable not in constraint
677
678
Returns:
679
float: Coefficient value
680
"""
681
682
def toDataclass(self):
683
"""
684
Convert constraint to dataclass representation.
685
686
Returns:
687
Dataclass representation of the constraint
688
"""
689
690
def fromDataclass(self, data):
691
"""
692
Load constraint from dataclass representation.
693
694
Parameters:
695
- data: Dataclass containing constraint data
696
"""
697
```
698
699
Usage examples:
700
701
```python
702
# Create constraints using operators
703
prob += x + 2*y <= 10 # Less than or equal
704
prob += 3*x - y == 5 # Equality
705
prob += x >= 0 # Greater than or equal
706
707
# Named constraints
708
constraint1 = LpConstraint(x + y, LpConstraintLE, "capacity", 100)
709
prob += constraint1
710
711
# Modify constraint
712
constraint1.changeRHS(120) # Change right-hand side to 120
713
```
714
715
### Linear Expressions
716
717
The LpAffineExpression class represents linear combinations of variables with constant terms, supporting arithmetic operations and solution value extraction.
718
719
```python { .api }
720
class LpAffineExpression:
721
def __init__(self, e=None, constant=0.0, name=None):
722
"""
723
Create a linear expression.
724
725
Parameters:
726
- e (dict or LpVariable, optional): Initial variable coefficients
727
- constant (float): Constant term
728
- name (str, optional): Expression name
729
"""
730
731
def addterm(self, var, coeff):
732
"""
733
Add a term to the expression.
734
735
Parameters:
736
- var (LpVariable): Variable to add
737
- coeff (float): Coefficient for the variable
738
"""
739
740
def copy(self):
741
"""
742
Create a copy of the expression.
743
744
Returns:
745
LpAffineExpression: Copy of the expression
746
"""
747
748
def value(self):
749
"""
750
Calculate the value of the expression using current variable values.
751
752
Returns:
753
float: Expression value (None if variables not solved)
754
"""
755
756
def valueOrDefault(self):
757
"""
758
Get expression value or default if variables not solved.
759
760
Returns:
761
float: Expression value or constant term
762
"""
763
764
@property
765
def constant(self):
766
"""Get or set the constant term."""
767
768
def emptyCopy(self):
769
"""
770
Create an empty copy of the expression.
771
772
Returns:
773
LpAffineExpression: Empty expression with same name
774
"""
775
776
def addInPlace(self, other):
777
"""
778
Add another expression to this expression in place.
779
780
Parameters:
781
- other (LpAffineExpression or LpVariable): Expression to add
782
"""
783
784
def isAtomic(self):
785
"""
786
Check if expression contains only one variable with coefficient 1.
787
788
Returns:
789
bool: True if expression is atomic
790
"""
791
792
def isNumericalConstant(self):
793
"""
794
Check if expression is a numerical constant (no variables).
795
796
Returns:
797
bool: True if expression contains no variables
798
"""
799
800
def atom(self):
801
"""
802
Get the single variable if expression is atomic.
803
804
Returns:
805
LpVariable: Single variable if atomic, None otherwise
806
"""
807
808
def sorted_keys(self):
809
"""
810
Get variables sorted by name.
811
812
Returns:
813
list: List of variables sorted by name
814
"""
815
816
def keys(self):
817
"""
818
Get variables in the expression.
819
820
Returns:
821
dict_keys: Variable keys in expression
822
"""
823
824
def values(self):
825
"""
826
Get coefficients in the expression.
827
828
Returns:
829
dict_values: Coefficient values in expression
830
"""
831
832
def items(self):
833
"""
834
Get variable-coefficient pairs in the expression.
835
836
Returns:
837
dict_items: Variable-coefficient pairs
838
"""
839
840
def get(self, var, default=0):
841
"""
842
Get coefficient for a variable.
843
844
Parameters:
845
- var (LpVariable): Variable to get coefficient for
846
- default (float): Default value if variable not in expression
847
848
Returns:
849
float: Coefficient value
850
"""
851
852
def clear(self):
853
"""
854
Clear all variables and constant from the expression.
855
"""
856
857
def toDict(self):
858
"""
859
Convert expression to dictionary representation.
860
861
Returns:
862
dict: Dictionary representation of the expression
863
"""
864
865
def toDataclass(self):
866
"""
867
Convert expression to dataclass representation.
868
869
Returns:
870
Dataclass representation of the expression
871
"""
872
```
873
874
Usage examples:
875
876
```python
877
# Create expressions
878
expr1 = 2*x + 3*y + 5 # Linear expression with constant
879
expr2 = LpAffineExpression([x, y], [2, 3], 5) # Equivalent creation
880
881
# Build expressions incrementally
882
expr = LpAffineExpression()
883
expr.addterm(x, 2)
884
expr.addterm(y, 3)
885
expr.constant = 5
886
887
# Use in constraints and objectives
888
prob += expr <= 100 # As constraint
889
prob += expr # As objective function
890
```
891
892
### Specialized Constraint Types
893
894
Additional constraint types for advanced modeling scenarios including fraction constraints and elastic formulations.
895
896
```python { .api }
897
class LpConstraintVar:
898
"""
899
Special constraint type for column-wise modeling where constraints
900
are treated as variables in the dual problem.
901
"""
902
def __init__(self, name=None, sense=None, rhs=None, e=None): ...
903
904
class LpFractionConstraint:
905
"""
906
Constraint enforcing fraction requirement: numerator/denominator = target.
907
Used for ratio optimization problems.
908
"""
909
def __init__(self, numerator, denominator, name=None, sense=LpConstraintEQ, rhs=None): ...
910
911
class FixedElasticSubProblem:
912
"""
913
Elastic constraint subproblem for handling infeasible constraints
914
by allowing violations with penalty costs.
915
"""
916
def reConstrain(self, newRHS): ...
917
918
class FractionElasticSubProblem:
919
"""
920
Elastic version of fraction constraints with violation penalties.
921
"""
922
def reConstrain(self, newRHS): ...
923
924
class LpElement:
925
"""
926
Base class for LpVariable and LpConstraintVar, providing common
927
functionality for problem elements.
928
"""
929
def __init__(self, name): ...
930
```
931
932
These specialized classes enable advanced modeling techniques including column generation, ratio optimization, and robust optimization with constraint relaxation.