Formulating optimization models inside traditional programming languages such as Python is very popular. The main tool the developers use to make this possible is operator overloading. There are cases, where we can write code that looks somewhat reasonable, is accepted and processed without any warning or error messages, but is total nonsense. It is rather difficult with this approach to make things airtight. Especially error handling. In [1], we see a good example. I have created a small fragment here that illustrates the problem.
import pulp # data n_orders = 2 m_rows = 2 cases = [1]*n_orders # Define the model model = pulp.LpProblem("Minimize_Total_Hours", pulp.LpMinimize) # Decision variables: binary variables for assigning each order to a row x = pulp.LpVariable.dicts("x", [(i, j) for i in range(n_orders) for j in range(m_rows)], 0, 1, pulp.LpBinary) # Additional variable: number of orders assigned to each row y = pulp.LpVariable.dicts("y", [j for j in range(m_rows)], 0, n_orders, pulp.LpInteger) # CPH based on the number of orders in a rowdef get_cph(num_orders): if num_orders == 1: return152elif num_orders == 2: return139elif num_orders == 3: return128elif num_orders == 4: return119elif num_orders == 5: return112elif num_orders == 6: return107else: return104# Objective function: Minimize total hours across all rows model += pulp.lpSum( (pulp.lpSum(cases[i] * x[i, j] for i in range(n_orders)) / get_cph(y[j])) for j in range(m_rows) ) print(model) # Solve the model model.solve() # print solve status pulp.LpStatus[model.status]
This code looks superficially like something reasonable. But it is total nonsense.
There are many issues here.
- PuLP is for linear problems only. So, division by a variable quantity is not allowed.
- We can't use if statements like this.
- Functions are not callbacks but are called at model generation time.
- The == inside the function is not a standard comparison operator but an operator hijacked by PuLP. It always returns true in this context. The first if is true, independent of the right-hand side. We could have used:
if num_orders == 3.14:
with the same results. - An expression like y[j]==1 is interpreted as a PuLP constraint.
''Optimal
How to confuse PuLP even more
if num_orders == 1:
by
if num_orders == "crazy":
you will see things like:
RecursionError: maximum recursion depth exceeded in comparison
Obviously, this is crazy.
Conclusion
References
- Pulp Constraint Problem with [i,j] integers not giving the best Solution, https://or.stackexchange.com/questions/12615/pulp-constraint-problem-with-i-j-integers-not-giving-the-best-solution
- Modeling surprises, https://yetanothermathprogrammingconsultant.blogspot.com/2024/05/modeling-surprises.html
- PuLP Mystery, https://yetanothermathprogrammingconsultant.blogspot.com/2020/10/pulp-mystery.html