Algebra of Zedfuncs

Within Envision, the term zedfunc is used to define a function $f: \mathbb{Z} \mapsto \mathbb{R}$. Zedfuncs are usually intended to represent the economic reward - or loss - associated to a numeric decision. For example, zedfuncs allow you to represent unit-dependent quantities such as the buy price determined by price breaks and the incremental reward function.

The zedfunc datatype

Zedfuncs are a native datatype of Envision. Similarly to ranvars, a zedfunc corresponds to a whole function rather than a single value. For example, below, we generate an affine function $f(x) = 2x+1$.

z := linear(2) + 1
y := valueAt(z, 3) // y = 7

The function valueAt takes a zedfunc as the first argument z and a number x as the second argument. This function returns the value of the zedfunc function at x. There are plenty of other ways of generating zedfuncs, which will be discussed in the following.

As zedfuncs are defined over $\mathbb{Z}$ - integers both positive and negative - a single zedfunc could hold, in theory, an arbitrarily large amount of information. However, computations over zedfuncs would become unbearably slow as the amount of data would increase. Thus, Envision has an internal upper bound on the amount of data that can be carried by a single zedfunc.

When an operation generates a zedfunc that exceeds its storage quota, the zedfunc is compressed (with loss) in order to fit under the storage quota. If $f^{*}$ is the compressed version of $f$, Envision attempts to minimize the loss function:

$$\mathtt{loss}(f , f^{* }) = \sum\limits_{k \in \mathbb{Z}} w(k) |f(k) - f^{* }(k)|\mathsf{.} $$

Where $w$ is a special weighting factor introduced to ensure the convergence of the sum. Intuitively, the lossy compression attempts to preserve the sharp edges of the original zedfunc.

Zedfuncs can be exported to Ionic files but not in flat text files. Due to the compression algorithms internally used by Envision, there is no “simple” representation of zedfuncs to be exposed in a plain text.

Plotting a zedfunc

The graph of a zedfunc can be obtained using a plot tile:

x := (linear(1) - 1) * (linear(1) - 2) - 20
show plot "F(x) = (x - 1) (x - 2) - 20" a1d6 tomato with x * uniform(-5, 10)

Here, we are defining x as a second-degree polynomial expression. linear(1) returns the identity function $f: x\mapsto x$. Thus, the expression x, as defined can be interpreted as the polynomial $F: x \mapsto (x-1)(x-2)-20$.

Finally, as a zedfunc is defined over the segment $[-\infty, +\infty]$, the default display of a zedfunc tends to be unsatisfying, as the zedfunc appears typically highly zoomed-out. Thus, we multiply by uniform() which zeroes out the zedfunc beyond the right and left boundaries passed as argument to uniform().

Generating zedfuncs

The simplest forms of zedfuncs are the parametric ones; that is, zedfuncs that are generated based on a formula. As such we have:

Beyond parametric zedfuncs, specific Envision functions allow you to generate parametric zedfuncs of the type identity and uniform. Zedfuncs are also the result of Envision functions such as stock reward and price breaks. In this case, the zedfunc is built based on the ranvar representing the demand forecast.

However, in practice, most of the zedfuncs are generated by combining existing zedfuncs through operators and transformations.

Point-wise operations

Envision supports point-wise operations on zedfuncs. For example, let $f$ and $g$ represent two zedfuncs $\mathbb{Z} \mapsto \mathbb{R}$. Then, we can define the addition as $f+g: k \mapsto f(k) + g(k)$. Within Envision, this is simply written as:

U = V + Z

It must be noted that in this example a vector zedfunc is created. Within Envision we are not typically processing a single zedfunc at a time, but a whole vector of zedfuncs at once, one for each item. However, the same operation can also be performed on scalar zedfuncs:

U := V + Z

Many basic operations on numbers have their native point-wise counterpart over zedfuncs:

U = V - Z // point-wise subtraction
U = V * Z // point-wise multiplication
U = maxz(V, Z) // point-wise max
U = minz(V, Z) // point-wise min

Finally, when performing operations over zedfuncs, regular numbers are automatically converted as zedfuncs by simply broadcasting their value over $\mathbb{Z}$; i.e. $f_a : k \mapsto a$ for a $a$ a number. This feature facilitates blending numbers and zedfuncs in the same expressions.

a := 42 // scalar number
U = V + a
U = V - a
U = V * a
U = V / a
U = maxz(V, a)

Transforming zedfuncs

A transformation is a function that takes a zedfunc as input and returns a new “transformed” version of the zedfunc. In particular, Envision supports the two following transformations that are handy when modeling economic rewards:

Indeed, the integral can be interpreted as the cumulative reward up to a certain target, while the derivative can be interpreted as the marginal reward at a certain target.

The operators >> (resp. <<) a zedfunc to the right (resp. to the left); that is:

$$f \gg a : k \mapsto f(k - a)$$ $$f \ll a : k \mapsto f(k + a)$$

which gets written in Envision as:

U = Z << a // left shift
U = Z >> a // right shift

where $a$ is an integer (a real number is approximated by Envision to its nearest integer). Naturally, if a is negative, then the shift operators keep working, but the left shift becomes a right shift, and vice versa.

Evaluating zedfuncs

As a rule of thumb, the goal is to maintain the zedfuncs as zedfuncs as long as possible. However, there are situations where the only practical option consists of converting the zedfunc back to numbers. Envision supports two functions to achieve this:

Assuming that the zedfunc has an affinity with a ranvar, the zedfunc can be visualized in a table, through extend.distrib as illustrated by:

p := poisson(5)
table G = extend.distrib(p)

r := stockrwd.m(p, 0.1)
G.R = int(r, G.Min, G.Max)

show table "My Grid" with Id, G.Min, G.Max, G.R