Distributions in Envision are practical because they are supported by an entire algebra of distributions. However, in order to generate inventory decisions, it is usually required to transform those distributions back into a grid
, that is, a table that enumerates the values of the distribution, typically probabilities. Using such a grid, it becomes possible to create the final table that contains the suggested purchase order quantities or the suggested inventory movements. Here, we detail how the function
can be used for this purpose.
Grids vs Distributions
is a plain table that happens to contain a list
of histogram buckets, one histogram per original distribution, and one line per bucket within the histogram. The generic representation of a grid is a table with four fields:
Id which identifies the item
Min, the lower inclusive boundary of the histogram bucket
Max, the higher inclusive boundary of the histogram bucket
Value, the value associated to the histogram bucket
For a given item, the grid may contains a series of buckets that represent the entire histogram of the distribution. If the distribution is a random variable
, the bucket value can be interpreted as the probability
of the distribution over the segment covered by the bucket.
Grids are not as flexible as distributions. For performance, it is frequently not possible to maintain buckets having a width of 1. Instead, larger buckets are used instead in order to keep the memory requirements of the grid manageable within Lokad. Non-unit buckets complicate calculations done on grids. Also, by design, grids have compact support
(in the mathematical sense): their non-zero values are only defined for a finite number of points.
On the other hand, grids are plain tables
and can be processed into other tables through the usual Envision operators. As a rule of thumb, Envision emphasizes an approach where all the economic and probabilistic modeling is done with distributions, keeping the transformation into grids as one of the final steps, just before generating the final suggested supply chain decisions.
Syntax of extend.distrib()
Envision distributions offer a powerful algebra, which can be used to avoid convoluted calculations of probabilities over lists. However, there are situations where having a raw list of probabilities is just fine, and even desirable. The distribution extension
turns a vector of distributions into a table, as illustrated by the following syntax:
table T = extend.distrib(D)
show table "Distribution details" with
is expected to be a vector of distributions, as typically produced by Lokad's probabilistic forecasting engine. Table
is typed as an extension of the originating tables, the implicit
table in the script above. Table
is populated with three fields:
T.Min: the inclusive integral lower boundary of the segment
T.Max: the inclusive integral higher boundary of the segment
T.Probability: the sum of the distribution over the inclusive range
Despite its name, the
field actually refers to the sum of the distribution over the bucket range which is returned.
For relatively compact distributions, segments have a length of 1, and hence
T.Min == T.Max
. However, if the distribution spreads over higher values, segments of length equal to 1 would end up generating possibly millions of lines, which becomes unmanageable. Hence, when facing such large-valued distributions, Envision auto-aggregates the distributions around larger segments. Algorithms are therefore fine-tuned to keep the size of the generated tables manageable.
always singles out the zero segment. As a result, the [0;0] segment always gets its own line in the generated table. This behavior is indeed helpful in many business situations where zero demand represents an edge case - such as infinite stock cover - which requires some dedicated logic.
Then, three additional overloads for
are provided in order to gain more control over the specific granularity of the generated table.
The first overload is intended to help build a purchase prioritization list while taking the current stock levels into account. The syntax is the following:
table T = extend.distrib(D, S)
The first argument
is as defined above. The second argument
is expected to be an integral number. When this second argument is present, the generated table always includes two lines dedicated to the two segments [0;0] and [1;S]. Further segments are auto-generated starting from S+1 as detailed above. The default value for this argument when left unspecified is zero.
In practice, argument
is frequently defined as the sum of the stock available plus the stock on order. Indeed, when reordering, only the demand probabilities which exceed the current stock level should be considered.
The second overload is intended for situations where lot multipliers
are involved. In these situations, the table should iterate over segments of specific sizes. The relevant syntax is:
table T = extend.distrib(D, S, M)
are as defined above. The third argument
is expected to be an integral number. It represents the desired segment length. Thus, the table includes the list of segments [0;0], [1;S], [S+1;S+M] [S+M+1;S+2M] … If
is zero, then the function falls back on auto-sizing the segments.
In practice, forcing segments whose length is equal to 1 would possibly lead to performance issues as the size of the table can be arbitrarily large. Thus, Envision can fall back on a multiple
instead. Using a multiple ensures that a lot multiplier
logic will keep working; while preserving sane limits on the number of lines to be generated.
As a rule of thumb, we suggest not to use this overload unless lot multipliers are involved; and when they are involved, it is suggested to keep
at zero for any item that does not have a specific lot multiplier.
The third overload is intended for situations where MOQs
are involved. In these situations, the table should iterate long enough to reach
certain desired values. The relevant syntax is:
table T = extend.distrib(D, S, M, R)
are as defined above. The fourth argument
is expected to be a non-negative integral number. It represents the desired max
value to be reached by the grid, that is, there will be a line where
is greater or equal to
. The default value for this argument when left unspecified is zero.
In practice, this argument is used to cope with large minimal order quantities (MOQs) constraints that can only be satisfied with the generated table iterates far each to cover the MOQ values.
As a rule of thumb, we suggest not to use this overload unless there are MOQs to be reached; and when they are involved, it is suggested to keep
as small as possible. A small
value does not prevent the table
from reaching higher values, it only ensure that larger values are reached.