1 point by s40racer Nov 10, 2023 | flag | 7 comments

Hello,
If I wish to include parameters, such as MOQ or Order Multiplier, into the forecast, would I include this as one of the actionrwd input parameters? If so, is there an exhaustive list of the parameters that can be fed into the actionrwd function, and also their usage?

Thank you.

bperraudin Nov 16, 2023 | flag

Hello,

The exhaustive list of parameters is available in the documentation page of the function actionrwd.reward, in the section function signature: https://docs.lokad.com/reference/abc/actionrwd.reward/.

However, this will not help you to integrate MOQs or Order Multipliers. These constraints cannot and should not be treated using actionrwd. Indeed, the main output of actionrwd is the probability distribution of the customer demand that is not covered yet by existing stock (on hand or on order). This has no reason to be affected by MOQ/Multiplier constraints.
However, once the demand left to satisfy is obtained through actionrwd, an economic optimization must be performed to determine the best decision to take, given that demand and other parameters/constraints. It is in this optimization that the MOQ/Mulitplier constraints must be taken into account.

If a product with a demand left to satisfy on the time period considered of 5 units has a MOQ of 50 units, the decision of whether or not you should actually purchase the MOQ completely depends on economical factors. If the product is expensive and has a low margin you might be reluctant to purchase it, if it is cheap and very profitable you might want to do it. This also possibly depends on other parameters as well like budget or storage limitation which can be integrated in an economical optimization.

I hope this helps,

s40racer Nov 16, 2023 | flag

Thank you. I apologize if my earlier question was not clear to begin with.

To clarify, I was asking from the perspective of the MOQ that I place on my customers, or if the customers have an MOQ or order multipliers when they purchase this item from me (due to logistics constraints or economy of scales on the transportation cost). In another words, I do not have an MOQ or order multiple to my customers, but they have instituted this requirement because of transportation efficiency and constraints.

How do I take these factors into account when generating a forecast? Currently, the forecasts are generated using the actionrwd.demand function. However there are no parameters to account for the MOQ or the order multiples, and the smoothed demand and the forecast are always way under-fitted for products with these requirements.

I hope this is clear.

bperraudin Nov 17, 2023 | flag

Ok now I understand better, sorry about my first irrelevant answer.
Unfortunately, action reward is not designed for these use cases. It assumes under the hood that the demand follows a negative binomial distribution defined by the mean and distribution in inputs.
For order multipliers, you could work you way around this limitation by converting every input of action reward (stock available, stock on order, baseline) in number of multipliers and then multiply the outputs by the size of the multiplier. This is far from perfect as you'll have to make rounding approximations during the conversions.
For MOQ, I'd say that action reward is not built for such use cases.

s40racer Nov 17, 2023 | flag

Thank you. Your previous answer is also relevant - it helped me understand more about the actionrwd function as a whole.

Are you able to provide coding example of how to (or how Lokad typically overcomes this limitation in real-world situations), for both the order multiplier and the MOQ? Not every product has the order multiplier or MOQ pattern or requirement.

And, similarly, if actionrwd is not the solution for this type of situation, how do you overcome this in general? Coding examples will also be very helpful here as well.

Thank you.

vermorel Nov 17, 2023 | flag

Hello! We have been developing - for the past two years - a general purpose stochastic optimizer. It has passed the prototype stage and we have a short series of client that are running their production over this new thingy. Stochastic optimization (aka an optimization under a noisy loss) is exactly what you are looking for here. This will be replacing our old-school MOQ solver as well.

We are now moving forward with the development of the clean long-term version of this stochastic optimizer, but it won't become generally available before the end of 2024 (or so). Meanwhile, we can only offer ad-hoc heuristics. Sorry for the delay, it has been a really tough nut to crack.

s40racer Nov 17, 2023 | flag

Thank you.

For this upcoming stochastic optimizer, could you describe the inputs/parameters data that is needed/required for the optimization to run?

bperraudin Nov 17, 2023 | flag

I completely agree with what was said in the previous comment: stochastic optimization will definitely enable to tackle this issue (and more) the right way.
Meanwhile, here is a piece of code that you could use to tackle the order multiplier problem, following the logic I described in my previous comment:


///## ACTION REWARD FUNCTION
///### Conversion to account for customer Order Multipliers
Skus.CustomerOrderMultiplier = 2
Skus.StockOnHandInLots = floor(Skus.StockOnHand/Skus.CustomerOrderMultiplier) //could also use round(), business assumption to be taken according to the pb treated
PO.OrderQtyInLots = floor(PO.OrderQty/Skus.CustomerOrderMultiplier)
CatalogPeriods.BaselineInLots = floor(CatalogPeriods.Baseline/Skus.CustomerOrderMultiplier)

///### Action reward call

Skus.WOOUncovDemandInLots, Skus.HoldingTime = actionrwd.reward(
  TimeIndex: CatalogPeriods.N
  Baseline: CatalogPeriods.BaselineInLots
  Dispersion: Skus.Dispersion ///assume Dispersion is adapted to Order Multiplier change. This should be handled in previous script
  Alpha: 0.05
  StockOnHand: Skus.StockOnHandInLots
  ArrivalTime: dirac(PO.POTimeIndex)
  StockOnOrder: PO.OrderQtyInLots
  LeadTime: Skus.SLT
  StepOfReorder: Skus.RLT)

Skus.WOOUncovDemand = Skus.WOOUncovDemandInLots * Skus.CustomerOrderMultiplier
Skus.SellThrough = (1-cdf(Skus.WOOUncovDemand + 1)) * uniform.right(1)

Regarding the customer MOQ, provided that the customers tend to always order the MOQ or a few units above the MOQ, you could use the same logic as an approximation and treat the MOQ as an Order multiplier. I can't offer a cleaner way to cope with this unfortunately, as it would require to dedicate too much time to the problem.