1
$\begingroup$

I am currently modelling a supply problem that attempts to optimise a rail schedule which moves products from a production plant, to a warehouse to satisfy sales.

The model is working fine (thanks in part to OR!) however I would like to create a variable that shows the extent of a supply shortfall. Currently if the current inventory is 0, and demand is > 0, then the model is unable to satisfy the constraints and returns 'Infeasible'.

I would like to define a slack/soft constraint that captures the shortfall so that the end result is always 'optimal' even though a shortfall is realised, and shows this extent.

Unfortunately my current soft constraint is not recognised by the model and the output is still 'infeasible'

Desired outcome:

desired_output_df.head(10)

>>>

date            product      current_inventory     sales_demand     inventory_shortfall
'2020-01-01'    'AFM'        10000                 5000             0
'2020-01-02'    'AFM'        5000                  5000             0
'2020-01-03'    'AFM'        0                     6000             6000


# Sales Demand
# Storage levels must meet sales demand
for date, grade in storage_inventory_vars:
  model += storage_inventory_vars[date, product] \
      + insufficient_storage_supply[(date, product)] \
      >= sales_demand[date][product]

The variable insufficient_supply[(date, product)] is the key slack constraint here that I would like to measure as it should prevent an infeasible solution (owing to insufficient supply to meet demand).

You can observe in the sales demand data on the 2020-05-18 and 19 that there is a very large spike in demand for AFE so that it greatly exceeds supply.

Here, if storage_inventory_vars['2020-05-18, 'AFE'] == 50,000 then insufficient_supply[('2020-05-18, 'AFE')] should == -50,000. The sum should then produce an optimal solution is the sum is greater than the demand.

All help very gratefully received, thank you.

$\endgroup$
7
  • $\begingroup$ Are you sure infeasibility is due to the demand constraint ? $\endgroup$
    – Kuifje
    Commented May 18, 2020 at 13:05
  • $\begingroup$ Hi @Kuifje, yes reasonably so. I run the model on dummy data that was currently optimal before the addition of this constraint. If I remove it entirely and adjust the sales demand so that supply > demand, an optimum is found. $\endgroup$
    – cmp
    Commented May 18, 2020 at 13:18
  • $\begingroup$ can you edit your post so that we can try to run the model (I think just the data is missing) ? $\endgroup$
    – Kuifje
    Commented May 18, 2020 at 13:34
  • $\begingroup$ @Kuifje sadly there is a whole lot more code to this model and I am not sure how to reduce it to the point where it constitutes an MRE. Apologies - I am aware this is convention on SO but its sadly not possible. I have added the sales demand data to give you an idea if this helps? $\endgroup$
    – cmp
    Commented May 18, 2020 at 13:44
  • $\begingroup$ Without all the data, I won't be able to reproduce the error. Lets try something else. Can you try setting "sales_demand[date][product]" to 0 in the last constraint ? $\endgroup$
    – Kuifje
    Commented May 18, 2020 at 14:02

2 Answers 2

3
$\begingroup$

The following constraints are infeasible :

 _C129: Rail_Loadings_From_Washplant_('2020_05_22',_'ABC',_'PRE')
        + Rail_Loadings_From_Washplant_('2020_05_22',_'ABC',_'ZBF')  = 25200

 _C134: Rail_Loadings_From_Washplant_('2020_05_23',_'ABC',_'PRE')
        + Rail_Loadings_From_Washplant_('2020_05_23',_'ABC',_'ZBF')  = 25200

 _C161: Rail_Loadings_From_Washplant_('2020_05_22',_'ABC',_'ZBF')
        + Port_Inventory_Levels_('2020_05_21',_'ZBF')
        - Port_Inventory_Levels_('2020_05_22',_'ZBF')  = 200000

 _C165: Rail_Loadings_From_Washplant_('2020_05_23',_'ABC',_'ZBF')
        + Port_Inventory_Levels_('2020_05_22',_'ZBF')
        - Port_Inventory_Levels_('2020_05_23',_'ZBF')  = 200000

 _C241: Port_Inventory_Levels_('2020_05_21',_'ZBF') <= 200000

I think there is a problem with your inventory equations. Not exactly sure where yet.

Finding the exact error is not that easy. Either there is a typo, either the model is not written correctly. My suggestion : back to basics, write the equations of the linear problem, and before anything, lets see if the model is properly written.

$\endgroup$
1
  • $\begingroup$ Thank you for your help @Kuifje! Hmm, C129 and C134 make sense (3 x 8400 which is the carrying capacity of the 3 trains) and so does C241 (max level) but from what I can see you are right about C134 and C161. I will check it out and the design too. What is peculiar is that the model worked perfectly before adding the slack constraint? $\endgroup$
    – cmp
    Commented May 18, 2020 at 20:44
1
$\begingroup$

I found the solution.

1- The storage inventory definition is as follows:

model += storage_stockpile_current[product] \
              + pulp.lpSum(
                    train_consignment_variables[(date, plant, product)] 
                    for plant in _plants_combo) \
              - sales_demand[date][product] \
              == storage_inventory_vars[(date, product)]

2 - Given the addition of the slack constraint:

for date, grade in storage_inventory_vars:
  model += storage_inventory_vars[date, product] \
      + insufficient_storage_supply[(date, product)] \
      >= sales_demand[date][product]

Whenever sales_demand greatly exceeds the storage variable, the equation becomes inbalanced, because it has a lower bound of 0, i.e. cannot be negative. Therefore the definition needs to reflect this slack constraint:

model += storage_stockpile_current[product] \
              + pulp.lpSum(
                    train_consignment_variables[(date, plant, product)] 
                    for plant in _plants_combo) \
              - sales_demand[date][product] \
              + insufficient_storage_supply[(date, product)] \
              == storage_inventory_vars[(date, product)]

A big thank you to Kuifje for their help.

$\endgroup$
1
  • 1
    $\begingroup$ Glad you found the bug! Cheers $\endgroup$
    – Kuifje
    Commented May 19, 2020 at 9:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.