Skip to content

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'...


WARNING: supply shed has excess demand, LP will not find solution unless option `allow_deviation=True` is used

    sources / supply

    FARM-1                       999
    FARM-2                      1000
    FARM-3                      1000
    total supply                2999

    sinks / demand

    PORT-3                      1000
    PORT-1                       400
    PORT-2                      1600
    total demand                3000

Warning: supply (2999) is insufficient to meet demand (3000), the solver might fail
/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'...


WARNING: supply shed has excess demand, LP will not find solution unless option `allow_deviation=True` is used

    sources / supply

    FARM-1                       999
    FARM-2                      1000
    FARM-3                      1000
    total supply                2999

    sinks / demand

    PORT-3                      1000
    PORT-1                       400
    PORT-2                      1600
    total demand                3000

Warning: supply (2999) is insufficient to meet demand (3000), the solver might fail
/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