List of functions

The primary purpose of functions is to extend the capabilities of Envision whenever the basic language syntax would not be sufficient. Below, we list the functions supported by Envision.

Function syntax

The general syntax for functions is:

x = fun(arg1, arg2, arg3, ..., argN) by [Group] sort [Order]

fun will be replaced by the name of the function and arg1, arg2, arg3, ..., argN will be replaced by the arguments passed to the function. All functions operate on vectors.

Only certain functions support the extra options by and sort. While the syntax is similar to the syntax of the aggregators, those functions are not aggregators. The by option is optional, and when it is omitted, it is equivalent to by 1. Both Group and Order support tuples of vectors, that is, multiple vectors delimited by commas. For example:

R = rank() by [A, B] sort [Items.1]

Mathematical functions

Text functions

It is important to keep in mind that text functions are case-sensitive. The ~ operator makes all text comparisons case-insenstitive, e.g., contains(foo, "bar") is case-sensitive, whereas contains(~foo, ~"bar") is case-insensitive.

In addition, there exist functions that enable to transform any element into text, e.g.,

Calendar functions

Ranking functions

Graph functions

Algebra of ranvars

In the following, given an integer-valued random variable $X: \mathbb{\Omega} \to \mathbb{Z}$, its associated probability distribution $\mathbf{P}[X = k]$ will be referred to as a ranvar. Additional information on the algebra of ranvars is provided in the algebra of ranvars section.

Parametric ranvars

Parametric ranvars can be generated through functions which take a number as an argument - the parameter.

Non-parametric ranvars

Indicators on ranvars

Numeric indicators about ranvars can also be obtained in Envision. If X and Y are independent random variables, with ranvars (probability distributions) R and S respectively, and a and b are integers,

Transformations of ranvars

A ranvar can also be transformed into another ranvar (the total mass of 1 being conserved by the transformation). If X and Y are independent random variables, with ranvars R and S respectively, and a and b are integers,

Algebra of zedfuncs

In Envision, a zedfunc is defined as a function $f: \mathbb{Z} \mapsto \mathbb{R}$. As an example, stockrwd.m() and pricebrk.m() return a zedfunc.

Parametric zedfuncs

Transformations of zedfuncs

Evaluations of zedfuncs

Table creation functions

New tables can be created from existing tables or ranvars:

Advanced functions

cumsub(G.Item, G.Stock, G.Quantity, G.Rank)

The cumsub() function explores all bundles by increasing rank, keeping track of the remaining stock for each item. It takes 4 vectors belonging to a grid table with:

Initially, this stock is defined by the G.Stock vector. For each bundle, the function determines whether there is enough remaining stock to purchase all grid lines in that bundle, based on whether the stock exceeds G.Quantity. If that is the case, then the function decrements the stock for each item, and writes to each grid line the remaining stock for that item. If there is not enough stock to serve the entire bundle - usually because one of the items has run out - then the function does not update the remaining stocks and stores for each grid line the value -(S+1) (where S is the remaining stock for that item at that point), to indicate both that the grid line is not purchased (test if G.S < 0) and whether it was that specific line that caused the bundle not to be purchased (test if G.Quantity + G.S + 1 > 0) and by how much (G.Missing = G.Quantity + G.S + 1).

cumsub.fallback(G.Item, G.Stock, G.Fallback, G.Quantity, G.Rank)

The cumsub.fallback() function explores all bundles by increasing rank, keeping track of the stock consumed for each item. Every bundle attempts to subtract the corresponding quantity from the remaining stock of the corresponding item. If insufficient, additional stock is taken from the fallback item.

The cumsub.fallback() function takes 5 vectors belonging to a grid table with:

If the bundle succeeds, the function returns for each bundle line the quantity of stock taken from the item’s stock. The quantity taken from the fallback item can be deduced by calculating Grid.Stock - Grid.Quantity. In the specific example where there is no remaining stock on the item to serve the bundle but enough on the fallback item, the function returns 0. Then, the quantity taken from the fallback item is exactly Grid.Quantity. If the bundle fails, the function returns -1 for each bundle line. Note that each fallback item must have an associated line in the grid, otherwise, its fallback stock will not be considered. If none, fake grid lines must be created with missing fallback items.

There are various possible applications of the cumsub.fallback() function, e.g.:

The last use case, namely, Identification of open orders covered by current stock on hand, is being picked up and presented below. It displays a specific example of a customer willing to identify which open orders are currently entirely covered by the stock of his main warehouse (all references inside the order must be covered by the main warehouse stock). Otherwise, those orders would be served by other locations. There is an additional constraint which allows every reference to have a fallback stock in case their own stock would not be sufficient to cover the demand. In this example, two items are considered, A and B. A is the fallback of B. There are five orders ranked by order date that are explored by increasing rank (one by one). If there is enough stock to cover all references of the first order, the whole order is considered covered: the stock of all the associated references is being reduced. If there is still a need, the stock of their fallback references is being decreased as well. The next order is being explored. The same logic applies.

In the table entitled: “Results: cumsub.fallback”: Image

two fields that represent the stock levels of a reference A and B require particular attention. The stock of A needs to be reduced even when the stock of B is required. As a rule of thumb, the stock of a reference with a fallback will be displayed as “Own Stock”, while the stock of a reference without any fallback will be displayed as “Fallback Stock”.

In the selected example, the first order requires 8 units of A, whereas the initial stock contains 10 units. Thus, this order can be easily served. The stock of A will be reduced to 2 units (see “Fallback Stock”). The second order requires 3 units of A, however, only 2 units are left. This order cannot be served. The “Own Consumption” is assigned a -1 consumption, and the stock of A remains the same. The third order requires 5 units of B, and B has 4 units of initial stock, and A has 2 remaining units: this order can be served. We reduce the stock of B which drops to 0 (see “Own Stock”), while the stock of A falls down to 1 unit (see “Fallback Stock”). At last, entirely covered orders are identified as those with either an own stock consumption or a fallback stock consumption strictly positive for every reference of those orders.

The following Envision code covers the selected use case:

read "/sample/Lokad_Items.tsv" as Items with
  Id : text
  Fallback : text
  Stock : number

read "/sample/Lokad_Orders.tsv" as Orders with
  Id : text
  OrderId : text
  Date : date
  OrderedQty : number

Orders.OrderIdRank = -1
where argfirst() by Orders.OrderId sort Orders.1
  Orders.OrderIdRank = rank() sort parsenumber(Orders.OrderId)
Orders.OrderIdRank = same(Orders.OrderIdRank) if(Orders.OrderIdRank != -1) by Orders.OrderId

//Cumsub fallback
//Variables initialization
Orders.Fallback = same(Fallback) by Id at Orders.Id
HasFallback = Id != Fallback

///Orders coverage calculation
Orders.StockConsumed = cumsub.fallback(Id, Stock, Fallback, Orders.OrderedQty, Orders.OrderIdRank)
Orders.FallbackStockConsumed = HasFallback & Orders.StockConsumed >= 0 ? Orders.OrderedQty - Orders.StockConsumed : 0
Orders.IsCovered = all(Orders.StockConsumed > 0 | Orders.FallbackStockConsumed > 0) by Orders.OrderId

//In Orders.Stock, we keep track of the stock of all references with a fallback item
Orders.CumStockConsumed = cumsum(HasFallback ? max(Orders.StockConsumed, 0) : 0) by Id sort Orders.OrderIdRank
Orders.Stock = (HasFallback ? Stock : 0) - Orders.CumStockConsumed

//In Orders.FallbackStock, we keep track of the stock of all references without a fallback item
Orders.CumFallbackStockConsumed = cumsum(HasFallback ? Orders.FallbackStockConsumed : max(Orders.StockConsumed, 0)) by Fallback sort Orders.OrderIdRank
Orders.FallbackStock = same(Stock) by Id at Orders.Fallback
Orders.FallbackStock = Orders.FallbackStock - Orders.CumFallbackStockConsumed

show table "Cumsub fallback" e3h3 with
  Orders.OrderIdRank as "OrderId Rank"
  Orders.OrderId as "OrderId"
  Id as "Ref"
  Fallback as "Fallback"
  HasFallback ? "Yes" : "No" as "Fallback Item?"
  Stock as "Own Initial Stock"
  Orders.OrderedQty as "Ordered Qty"
  Orders.StockConsumed as "Own Consumption"
  Orders.FallbackStockConsumed as "Fallback Consumption"
  Orders.Stock as "Own Ctock"
  Orders.FallbackStock as "Fallback Ctock"
  Orders.IsCovered ? "Yes" : "No" as "Covered?"
  order by Orders.OrderIdRank


The function extend.distinct takes up to 8 tables as arguments - to be passed to T1, T2, etc. with each table associated to a varying number of text vectors. All those text values are gathered and deduplicated in order to return a single table that consolidates all those values.

table T = extend.distinct(
  T1: TA.VA, TA.VB, ...
  T2: TB.VA, TB.VB, ...
  T3: ...)

show table "Merged" with T.Value

forex(value, Origin, Destination, date)

Returns the amount expressed in the currency Origin into the equivalent amount in the currency Destination according to the historical rates at the specified date. The currencies should be encoded with their canonical three-letter codes. Lokad supports about 30 currencies leveraging the data provided by the European Central Bank. Rates are mostly updated on a daily basis. See also isCurrency() to test the validity of your currency code.

forex.last(Origin, Destination)

Returns the date of the latest exchange rate found for a pair of currencies. The currencies should be encoded with their canonical three-letter codes. Not all exchange rates are uploaded daily to the European Central Bank.


Returns a pseudo-injective hash value between 0 and 2^24-1. This function is typically used to randomly shuffle a dataset by hashing the content of a column, and then sorting against the hashed values.


Returns true if the text entry passed as an argument is a currency code recognized by the forex() function.

mkuid(X, offset)

Returns a unique number, with unicity maintained across Envision runs. This function is intended to uniquely identify results calculated by Lokad. For example, it can be used to generate a unique purchase order number to be incremented whenever the Envision script is re-executed.

The vector X is ignored, but the UID (unique identifier) is generated as a scalar in the table associated to X. The offset is an optional scalar that represents the starting suffix for the UID. The generated strings are numbers in the format PPPPPPPAAA, with P as a page number (does not start with 0) that is always strictly increasing, and A as an incremented counter that starts at offset (or 0 if no offset parameter is provided). P has at least 7 digits, A has at least 3.

The UIDs offer three properties.

  1. All UIDs can be parsed as numbers, and those numbers will be different. Keep in mind, however, that UIDs have at least 10 digits, and likely more if each call needs to generate more than 1000.
  2. A UID generated at time T is strictly inferior (in alphabetical order) to a UID generated at time T’ > T.
  3. If all calls generate similar numbers of UIDs (less than 999, or between 1000 and 9999, etc.) then the previous property is also true for the numeric order between UIDs.


An advanced numeric solver for the general MOQ problem (minimal order quantities).


Merchant variant of the price break function. Returns the zedfunc representing the marginal purchase unit price from a table listing the price breaks. See Supplier Price Breaks.


Fiscal variant of the price break function. Returns the zedfunc representing vector of the marginal purchase unit price from a table listing the price breaks. See Supplier Price Breaks.

priopack(V, MaxV, JT, B) by [Group] sort [Order]

A simple variant of the bin packing algorithm intended to be used with a purchase prioritization list. Unlike the classic bin packing algorithm, not only do we seek to optimize the bin capacities, but the ordering of the units will also be preserved as much as possible.


This call-function is intended to convert time-series into probability distributions (ranvar) by collecting observations over moving windows.

D = ranvar.segment(
  start: Items.Start // first date (inclusive) for each item
  end: Items.End // end date (inclusive) for each item
  step: Items.Step // number, increments in day in-between observation
  horizon: Items.Horizon // number, the length in day of period for each item
  date: Orders.Date  // date for each event
  censoredDemandDate: Censored.Date // days that are skipped when generating the ranvar
  quantity: Orders.Quantity) // quantity for each event

This function computes, for each item, the ranvar of the sum of event quantities over periods of horizon length, that are entirely between the first and last date for that item. For example, for a start date on Jan 1st, end date on Jan 7th, a horizon of 3 days, and a single event of quantity 5 on Jan 2nd, the observed periods are:

Thus, the resulting ranvar is 60% Q = 0, 40% Q = 5.

When using the censoredDemandDate: argument, the censored days are skipped, i.e., the ranvar is generated as if these days never existed. Using the previous example, if the 3rd of January is censored, the ranvar segment is applied as if the 4th of January was the day following the 2nd of January.

Thus, the resulting ranvar is 50% Q = 0, 50% Q = 5.


This is a more general version of ranvar.segment, where the horizon is a ranvar of lengths (e.g. the probability distribution of lead times) and events at the extrema or at the middle of the chosen time interval are equally taken into account:

D = ranvar.periodicr(
  start: Items.Start // first date (inclusive) for each item
  end: Items.End // end date (inclusive) for each item
  horizon: Items.LeadTime // ranvar of lengths in day
  date: Orders.Date  // date for each event
  quantity: Orders.Quantity) // quantity for each event

Indeed, ranvar.periodicr considers an infinite repetition of the input data and sums the event quantities over periods of all possible lengths contained in the horizon ranvar. As a consequence, if we consider the example above and we imagine an additional event of quantity 2 on Jan 7th, ranvar.periodicr with dirac(3) as the horizon would observe the following:

and returns the ranvar ~28% Q = 0, ~28% Q = 2, ~28% Q = 5, ~14% Q = 7. For comparison, ranvar.segment with horizon 3 and step 1 would ignore the last two lines of the above list and return 40% Q = 0, 20% Q = 2, 40% Q = 5.


Gives access to a random forest regression algorithm. A training dataset (Example) and an evaluation dataset (Sample) must be provided. These two tables contain the same series of column attributes, for a different set of Id. In addition, the Example table contains a further column called Label, which represents the desired output of the regression function. The Example table shall thus be conceived as a Sample with a Label.

The function syntax is the following:

Sample.Label = forest.regress(
  training: Example.A, Example.B, Example.C // number/boolean/text
  trainingBow : Example.D // plain text, optional
  label: Example.Label // number
  evaluation: Sample.A, Sample.B, Sample.C // number/boolean/text
  evaluationBow: Sample.D) // plain text, optional

This call-function returns for each Id a ranvar (probability distribution) representing the quantity Label. Up to 16 different attributes are supported as training and evaluation entries. Of these, 8 at most can be number vectors and 8 at most can be Boolean or text vectors representing categories. Optionally, a text vector of words can be provided as trainingBow and evaluationBow: here the text string is treated as a bag-of-words, and analysed in terms of words occurrences.

smudge(values, present) by [Group] sort [Order]

Takes an incomplete vector of values and a Boolean vector that determines where the valid values are present. It returns a full vector of valid values, which has been completed by spreading valid values into the non-valid ones. More precisely, the output vector is built by looking at every line, group by group (if there is a Group argument) and following the ascending Order, and replacing any non-valid value by the last value that has been seen, or by a default value if no valid value has yet been seen in the group.

stockrwd.m(D, AM), stockrwd.s(D), stockrwd.c(D, AC)

The stock reward functions are used to build prioritized ordering policy out of the probabilistic forecasts produced by Lokad.