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 to make things airtight. 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]
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.
So what does PuLP say about all these problems? Absolutely nothing.
When running this, we see:
''Optimal
The values 0.006578947368421052 are 1/152. That's not at all what the author expected.
A good rule in well-designed tools is: "anything that is not forbidden, is allowed". I would like to see alarms going off for input like this.
Other PuLP surprises are in [2,3].
How to confuse PuLP even more
If we replace
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
Computing is still in the Stone Age.
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