Subranges

A subrange is a dimension that is defined using elements that are all part of another dimension. For example, if there is a dimension Store, with elements Downton, Glendale, Landsdown, Whyte then a subrange would be the dimension StoreWithBakery with elements Downtown, Landsdown. Nothing is required to specify that StoreWithBakery is a subrange of Store - the software can determine that from the names of the elements. However, because StoreWithBakery is subrange, you easily combine equations using Store and StoreWithBakery without having to create detailed equations relating the two.

Subranges do not need to be contiguous elements in the full range, nor do they need to be in the same order. For example, defining BackStore as Whyte, Landsdown, Glendale, Downtown could be used to reverse element order. In this case BackStore is a subrange of Store, and Store is a Subrange or BackStore (and StoreWithBakery is a subrange of both).

Note The purpose of subranges is to simplify equations. Sticking to a naming convention will also help make them clear.

Superranges

A superrange is the converse of a subrange - all elements of the subrange are contained in the superrange. The superrange is often called the parent dimension, but need not be the only one. For example

product : ham, cheese, milk, screwdriver, hammer

grocery : ham, cheese, milk

dairy : milk, cheese

tool : screwdriver, hammer

In this case grocery, dairy and tool are all subranges of product, which is the parent dimension. But dairy is also a subrange of grocery, and both grocery and product are superranges of dairy.

Using arrays defined by the parent dimension

If you have variables that are applicable only to a subset of the elements in a dimension, you can define a subrange of that dimension and use that subrange as the dimension for the variables. For example, anything related to baking might use the dimension StoreWithBakery instead of Store.

If you created the dimensions and equations this way, and want to use a variable arrayed by the full dimension in an equation arrayed by the subrange, you can do it by using the variable name and then the subrange name in brackets.

For example, suppose sales is arrayed by [Product, Store] you might have

planned_production[BakedProduct,StoreWithBakery] = sales[BakedProduct,StoreWithBakery]

Here BakedProduct is a subrange of Product, and StoreWithBakery is a subrange of Store. The above equation will appropriately select the correct elements of Product and Store to defined planned_production. This is done by matching the names of the different elements, and is the same as creating a non-apply-to-all set of equations with:

planned_production[Bread,Downtown] = sales[Bread,Downtown]

planned_production[Bread,Landsdown] = sales[Bread,Landsdown]

planned_production[Roll,Downtown] = sales[Roll,Downtown]

planned_production[Roll,Landsdown] = sales[Roll,Landsdown]

planned_production[Croissant,Downtown] = sales[Croissant,Downtown]

planned_production[Croissant,Landsdown] = sales[Croissant,Landsdown]

Using the subrange, in this case, both simplifies the writing of the equations, and allows you to keep the equation even when you make changes to the definitions of the dimensions involved.

To use a subrange in an equation, that subrange must be a dimension of the variable being defined (just as with any range). The ability to use that subrange in a variable that has the full range (or a bigger subrange) of the dimension is what is distinct.

To use variables with different dimensions, you must explicitly use the subrange. The equation

sales

for example would be invalid because sales is not arrayed by [BakedProduct,StoreWithBakery].

Using arrays defined by the subrange dimension

Continuing with the above example suppose that we are trying to determine utility costs at the stores, but that baking_utilties has been computed only for StoreWithBakery. We can still use it in the equation for utility_cost as in

lighting_cost[Store]+heating_cost[Store]+refrigeration[Store]+baking_utilities[Store]

For Downtown baking_utilities would be added to utility_cost sine Downtown is an element of StoreWithBakery. For Whyte, however, there is no match - so the software treats this as an invalid element index which by convention returns 0. Thus the aboe equation works for all stores without needing to write separate equations for each dimension of complex IF THEN ELSE logic.

You can apply this same logic inside of array expressions. For example total_sales across stores might be

SUM(produce_sales[*,Product]) + SUM(cheese_sales[*,Product]) + SUM(bakery_sales[*,Product])

where each of produce_sales, cheese_sales and bakery_sales is defined only for a subrange of Product. In this case those subranges would not overlap, but they could if it made sense.

Using Subranges in Array Builtins

When you use an array builtin such as SUM, you can specify the elements in the array to sum over by using *, a:b, or *:subrange as detailed in Specialized Array Manipulation. For example

sales_in_stores_with_bakery[Product] = SUM(sales[Product,StoreWithBakery.*])

would give the sales for all products (not just baked goods) in the stores that have bakeries. Because elements in the subranges do not need to be contiguous, this is much more flexible than using the a:b notation.

Using CONTAINS to distinguish elements of a subrange

The CONTAINS keyword can be used to specify what happens to elements that are part of a subrange. For example suppose that stores with bakeries are open longer than stores without. Then you could have an equation such as:

open_hours[Store] = IF StoreWithBakery CONTAINS Store THEN 12 ELSE 10

This equation results in the same thing as

open_hours[Store] = IF Store = Landsdown or Store = Downtown then 12 else 10

In addition to being more compact, the first equation does not need to change if a store is added to StoreWithBakery, this makes maintenance much easier.

In general, equations using CONTAINS and even if then else, are more readable than defining every element independently.

Using adjacent array elements

It is possible to use adjacent array elements by adding an offset. For example:

aging_in[age] = aging_out[age-1]

This will make aging in for A2 equal to aging_out for A1 and so on. It is actually the same as

aging_in[age] = IF age = A1 Then 0 ELSE aging_out[age-1]

This is because the convention for out of range array eleemnts is to return 0. The second equation, though somewhat longer, has the advantage of making that 0 more specific.

Mapping unrelated arrays

To map unrelated arrays you can make use of the fact that XMILE allows numeric specification of array elements. For example, suppose there are two arrays A and B both with 3 elements, and for a variable arrayed by A you want to make use of another variable arrayed by B. Further suppose that A1 corresponds to B2, A2 to B1 and A2 to B3. Then you could define

a_b_map[A] = 2,1,3

(as a non-a2a equation) then use

var2[A]= var1[a_b_map[A]]

To get the results you want, when var1 is arrayed by B.