7 LP function
View or edit on GitHub
This page is synchronized from trase/models/lp/7_LP_function.ipynb. Last modified on 2026-05-06 16:54 CEST by Trase Admin.
Please view or edit the original file there; changes should be reflected here after a midnight build (CET time),
or manually triggering it with a GitHub action (link).
LP function
import pandas as pd
from pulp import getSolver
from trase.tools.sps import solve_transportation_problem
df_supply = pd.DataFrame(
data=[("FARM-1", 999), ("FARM-2", 1000), ("FARM-3", 1000)],
columns=["production_trase_id", "production_vol"],
)
df_demand = pd.DataFrame(
data=[
("PORT-1", 400, "SOYBEAN OIL"),
("PORT-2", 1600, "SOYBEAN CAKE"),
("PORT-3", 1000, "SOYBEANS"),
],
columns=["port_trase_id", "export_vol", "product"],
)
df_costs = pd.DataFrame(
data=[
# Farm 1 is closest to port 1
("FARM-1", "PORT-1", 10),
("FARM-1", "PORT-2", 100),
("FARM-1", "PORT-3", 100),
# Farm 2 is closest to port 2
("FARM-2", "PORT-1", 100),
("FARM-2", "PORT-2", 10),
("FARM-2", "PORT-3", 100),
# Farm 3 is closest to port 3
("FARM-3", "PORT-1", 100),
("FARM-3", "PORT-2", 100),
("FARM-3", "PORT-3", 10),
],
columns=["origin_trase_id", "destination_trase_id", "cost"],
)
df_demand.set_index(["port_trase_id", "product"])["export_vol"]
port_trase_id product
PORT-1 SOYBEAN OIL 400
PORT-2 SOYBEAN CAKE 1600
PORT-3 SOYBEANS 1000
Name: export_vol, dtype: int64
supply = df_supply.set_index("production_trase_id")["production_vol"]
demand = df_demand.set_index(["port_trase_id", "product"])["export_vol"]
costs = df_costs.set_index(["origin_trase_id", "destination_trase_id"])["cost"]
commodity_ratios = {
"SOYBEANS": 0, # a ratio of 0 is used for primary commodity
"SOYBEANS CAKE": 0.8,
"SOYBEAN OIL": 0.2,
}
df_solution, df_leftover_supply = solve_transportation_problem(
supply=supply,
demand=demand,
costs=costs,
commodity_ratios=commodity_ratios,
solver=getSolver("GLPK_CMD"),
allow_deviation=False,
)
GLPSOL--GLPK LP/MIP Solver 5.0
Parameter(s) specified in the command line:
--cpxlp /tmp/8cffad1b4b4c43abb9354dca37585ad5-pulp.lp -o /tmp/8cffad1b4b4c43abb9354dca37585ad5-pulp.sol
Reading problem data from '/tmp/8cffad1b4b4c43abb9354dca37585ad5-pulp.lp'...
12 rows, 12 columns, 30 non-zeros
18 lines were read
GLPK Simplex Optimizer 5.0
12 rows, 12 columns, 30 non-zeros
Preprocessing...
12 rows, 12 columns, 30 non-zeros
Scaling...
A: min|aij| = 2.000e-01 max|aij| = 1.000e+00 ratio = 5.000e+00
Problem data seem to be well scaled
Constructing initial basis...
Size of triangular part is 12
0: obj = 2.100000000e+05 inf = 3.200e+03 (3)
7: obj = 2.818560000e+05 inf = 2.000e+00 (2)
LP HAS NO PRIMAL FEASIBLE SOLUTION
glp_simplex: unable to recover undefined or non-optimal solution
If you need actual output for non-optimal solution, use --nopresol
Time used: 0.0 secs
Memory used: 0.0 Mb (39693 bytes)
Writing basic solution to '/tmp/8cffad1b4b4c43abb9354dca37585ad5-pulp.sol'...
[33mWARNING: supply shed has excess demand, LP will not find solution unless option `allow_deviation=True` is used[0m
[33m[0m
[33m sources / supply[0m
[33m[0m
[33m FARM-1 999[0m
[33m FARM-2 1000[0m
[33m FARM-3 1000[0m
[33m total supply 2999[0m
[33m[0m
[33m sinks / demand[0m
[33m[0m
[33m PORT-3 1000[0m
[33m PORT-1 400[0m
[33m PORT-2 1600[0m
[33m total demand 3000[0m
[33m[0m
[33mWarning: supply (2999) is insufficient to meet demand (3000), the solver might fail[0m
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:275: FutureWarning: The provided callable <built-in function sum> is currently using np.sum. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string np.sum instead.
.apply(sum)
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:346: FutureWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
infeasible = df.groupby("sink_group").apply(
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
Cell In[13], line 11
3 costs = df_costs.set_index(["origin_trase_id", "destination_trase_id"])["cost"]
5 commodity_ratios = {
6 "SOYBEANS": 0, # a ratio of 0 is used for primary commodity
7 "SOYBEANS CAKE": 0.8,
8 "SOYBEAN OIL": 0.2,
9 }
---> 11 df_solution, df_leftover_supply = solve_transportation_problem(
12 supply=supply,
13 demand=demand,
14 costs=costs,
15 commodity_ratios=commodity_ratios,
16 solver=getSolver("GLPK_CMD"),
17 allow_deviation=False,
18 )
File /mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:123, in solve_transportation_problem(supply, demand, costs, solver, on_missing, on_lp_error, allow_deviation, eq_constraints, geq_constraints, leq_constraints, commodity_ratios, solve_kwargs)
108 return _solve_transportation_problem(
109 supply=supply,
110 demand=demand,
(...)
120 solve_kwargs=solve_kwargs,
121 )
122 else:
--> 123 return _solve_transportation_problem(
124 supply=supply,
125 demand=demand,
126 costs=costs,
127 solver=solver,
128 on_missing=on_missing,
129 on_lp_error=on_lp_error,
130 allow_deviation=allow_deviation,
131 eq_constraints=eq_constraints,
132 geq_constraints=geq_constraints,
133 leq_constraints=leq_constraints,
134 commodity_ratios=commodity_ratios,
135 solve_kwargs=solve_kwargs,
136 )
File /mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:496, in _solve_transportation_problem(supply, demand, costs, solver, on_missing, on_lp_error, allow_deviation, eq_constraints, geq_constraints, leq_constraints, commodity_ratios, solve_kwargs)
494 LOGGER.warning(message)
495 else: # "raise" or something unrecognised
--> 496 raise RuntimeError(message)
498 # loop over the LpVariables and fetch the original (source, sink) objects
499 df_allocation = pd.DataFrame(
500 {"index": int(v.name[1:]), "quantity": v.varValue}
501 for v in problem.variables()
502 if v.name[0] == "v"
503 )
RuntimeError: LP Status was not optimal: Undefined
df_solution, df_leftover_supply = solve_transportation_problem(
supply=supply,
demand=demand,
costs=costs,
commodity_ratios=commodity_ratios,
solver=getSolver("GLPK_CMD"),
allow_deviation=True,
)
GLPSOL--GLPK LP/MIP Solver 5.0
Parameter(s) specified in the command line:
--cpxlp /tmp/d1d0e0a7b3dd4e398d10ab535c98e99c-pulp.lp -o /tmp/d1d0e0a7b3dd4e398d10ab535c98e99c-pulp.sol
Reading problem data from '/tmp/d1d0e0a7b3dd4e398d10ab535c98e99c-pulp.lp'...
12 rows, 12 columns, 30 non-zeros
18 lines were read
GLPK Simplex Optimizer 5.0
12 rows, 12 columns, 30 non-zeros
Preprocessing...
12 rows, 12 columns, 30 non-zeros
Scaling...
A: min|aij| = 2.000e-01 max|aij| = 1.000e+00 ratio = 5.000e+00
Problem data seem to be well scaled
Constructing initial basis...
Size of triangular part is 12
0: obj = 2.100000000e+05 inf = 3.200e+03 (3)
7: obj = 2.818560000e+05 inf = 2.000e+00 (2)
LP HAS NO PRIMAL FEASIBLE SOLUTION
glp_simplex: unable to recover undefined or non-optimal solution
If you need actual output for non-optimal solution, use --nopresol
Time used: 0.0 secs
Memory used: 0.0 Mb (39693 bytes)
Writing basic solution to '/tmp/d1d0e0a7b3dd4e398d10ab535c98e99c-pulp.sol'...
GLPSOL--GLPK LP/MIP Solver 5.0
Parameter(s) specified in the command line:
--cpxlp /tmp/1b7e9f849f15466da07f8c1a26677e32-pulp.lp -o /tmp/1b7e9f849f15466da07f8c1a26677e32-pulp.sol
Reading problem data from '/tmp/1b7e9f849f15466da07f8c1a26677e32-pulp.lp'...
15 rows, 15 columns, 45 non-zeros
21 lines were read
GLPK Simplex Optimizer 5.0
15 rows, 15 columns, 45 non-zeros
Preprocessing...
15 rows, 15 columns, 45 non-zeros
Scaling...
A: min|aij| = 2.000e-01 max|aij| = 1.000e+00 ratio = 5.000e+00
Problem data seem to be well scaled
Constructing initial basis...
Size of triangular part is 15
0: obj = 0.000000000e+00 inf = 3.000e+03 (3)
3: obj = 3.000000000e+12 inf = 0.000e+00 (0)
* 17: obj = 1.000119846e+09 inf = 0.000e+00 (0)
OPTIMAL LP SOLUTION FOUND
Time used: 0.0 secs
Memory used: 0.0 Mb (39693 bytes)
Writing basic solution to '/tmp/1b7e9f849f15466da07f8c1a26677e32-pulp.sol'...
[33mWARNING: supply shed has excess demand, LP will not find solution unless option `allow_deviation=True` is used[0m
[33m[0m
[33m sources / supply[0m
[33m[0m
[33m FARM-1 999[0m
[33m FARM-2 1000[0m
[33m FARM-3 1000[0m
[33m total supply 2999[0m
[33m[0m
[33m sinks / demand[0m
[33m[0m
[33m PORT-3 1000[0m
[33m PORT-1 400[0m
[33m PORT-2 1600[0m
[33m total demand 3000[0m
[33m[0m
[33mWarning: supply (2999) is insufficient to meet demand (3000), the solver might fail[0m
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:275: FutureWarning: The provided callable <built-in function sum> is currently using np.sum. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string np.sum instead.
.apply(sum)
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:346: FutureWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
infeasible = df.groupby("sink_group").apply(
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:275: FutureWarning: The provided callable <built-in function sum> is currently using np.sum. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string np.sum instead.
.apply(sum)
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:346: FutureWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
infeasible = df.groupby("sink_group").apply(
/mnt/custom-file-systems/efs/fs-049d752ef37739434/shared/shared_repos/TRASE/trase/tools/sei_pcs/pandas_lp.py:532: FutureWarning: The provided callable <built-in function sum> is currently using np.sum. In a future version of pandas, the provided callable will be used directly. To keep current behavior pass the string np.sum instead.
df_allocation.groupby(["source"])["quantity"].apply(sum).to_frame()
df_solution
production_trase_id port_trase_id product
FARM-1 PORT-1 SOYBEAN OIL 199.8
PORT-2 SOYBEAN CAKE 799.2
FARM-2 PORT-1 SOYBEAN OIL 199.2
PORT-2 SOYBEAN CAKE 800.8
FARM-3 PORT-3 SOYBEANS 1000.0
Name: export_vol, dtype: float64
df_demand
| port_trase_id | export_vol | product | |
|---|---|---|---|
| 0 | PORT-1 | 400 | SOYBEAN OIL |
| 1 | PORT-2 | 1600 | SOYBEAN CAKE |
| 2 | PORT-3 | 1000 | SOYBEANS |