Array Functions and Operators

The @ Operator

When applied to an index, the @ operator returns the position of each element. @I is the 1-D array [1, 2, 3, 4,...] indexed by I. This is equivalent to Cumulate(1,I), but much more convenient.

The syntax A[@I=x] is a shorthand for Slice(A, I, x), just as A[I = x] is a shorthand for Subscript(A, I, x).

If I and J are indexes of the same length but different elements, and A is indexed by I, you can re-index I by J using A[@I=@J]. This is a bit more convenient than Slice(A, I, Cumulate(1, J)).

To lookup the position of a single element in an index, use @[I = x]. This quietly returns 0 if x is not in I. The test @[I = x] > 0 returns True if the element «x» is in «I», False if not.

The convenient subscripting syntax, A[I = x], is very convenient when you are dealing with your own indexes inside variable nodes. You know if two indexes have the same elements, and therefore if you can reindex using A[I = J]. However, when creating user-defined functions, you often want to avoid making strong assumptions about what the element values of an index are. You end up with a more flexible function if you only make assumptions about the length of indexes, and not their contents. In these situations, positional operations result in more flexible functions. Furthermore, if you need to create functions that work when there are duplicate elements, than again, positional operations are necessary. The @ operator tremendously shortens and simplifies functions that manipulate arrays positionally. While you have to know what the @ operator does, @I is no more obtuse than Cumulate(1, I) -- a trick used regularly and quite mysterious to someone who hasn't seen it previously.

Slice Assignment

You can assign a value to a single slice of a local variable, in any of these ways:

a[I = x] := v
Subscript(a, I, x) := v
a[@I = n] := v
Slice(a, I, n) := v

There are two limitations: As with normal assignment, a must be a local variable: Analytica does not allow side effects to global variables. And x or n must be a scalar (single value). It may not be an array.

However, it is possible for a to have two or more dimensions. If a is indexed by i and j, one of the assignments above will set the slice with the given value of i to v for all values of j.

If a is not already indexed by i, the assignment will expand it to have that index, repeating the previous value of a for all values of i except the one that is assigned. If v is an array with one or more indexes not already in a, the slice assignment will add the index(es) as dimensions of a. Again, it will repeat the previous values of a over the new index(es).

The assignment avoids making internal copies of the array values when possible. Since arrays and parts of arrays can be shared amongst values, copies or partial copies must sometimes be made when a slice assignment is evaluated. However, once the copy is made on the first such assignment, that portion of the array is usually no longer shared, so successive assignments are highly efficient and don't need to make excessive copies. This efficiency makes the implementation of many algorithms practical in Analytica that may not have been with previous releases.

Slice assignment returns as its result the value of v (not the value of the array).

Analytica 4.1 allows only one level of slicing or subscripting. I.e., you can do a[I=x] :=v but not a[I=x, J=y] :=v. Analytica 4.2 and later allows any number of slicing/subscripting levels.

For more details, see Slice assignment.

Subscript [I = x] operator

[I = x] acts as an operator, and can follow any expression. Previously it could only augment a variable identifier. As an expression, it can follow an expression such as (A + B)[I = x], or a function call f(A, B)[I = x], or even another subscript: A[I = x][J = y]. This operator has the highest precedence of any operator except the dot operator. So A^B[I = x] is A^(B[I = x]), etc., but A.I[I = x] is (A.I)[I = x].

The dynamic time offset variation, [Time-k], still only associates with a variable identifier. Thus, an expression such as (A + B)[Time - k] would not be accepted. You would have to write A[Time - k] + B[Time - k], at which point Analytica would recognize the dependence from A and B as a dynamic dependency, and render the corresponding arrow in gray.

Rank

Rank(X : all Vector[I] ; I : optional Index ; type : optional atomic numeric=-1)

The rank function has been extended with an optional type parameter, which may take the following values:

• -1 = lower-rank (default)
• 0 = mid-rank
• 1 = upper-rank

The rank type determines how values in X that occur multiple times are ranked. For example, if the value x = 5 occurs 6 times, and there are 3 other values in X that are less than 5, then x = 5 appears in the sort-order at positions 4, 5, 6, 7, 8, and 9. The lower-rank of x = 5 is 4, the upper-rank of x = 5 is 9, and the mid-rank is 6.5.

ArgMin and ArgMax

You can now specify multiple indexes, in which case a coordinate vector is returned. Also, you can optionally have the index position (rather than the index value) returned.

The ArgMin and ArgMax functions have the following parameter declarations:

ArgMinR: vector[I]; I: ... optional IndexType; position: optional boolean)
ArgMax(R: vector[I]; I: ... optional IndexType; position: optional boolean)

Example usages:

ArgMin(R: Cost, I: Bid)
ArgMax(R: Profit, I: UnitPrice, InvestmentLevel, DoTest)
ArgMax(R: OneDArr) /* Not recommended */

When only one index is provided (or when no index is provided but the parameter is 1-D), returns the index value at which the smallest (ArgMin) or largest (ArgMax) value of R occurs. In the event of ties, the element occurring closest to the end of index I is returned.

When more than one index is specified, returns a coordinate at which the smallest (ArgMin) or largest (ArgMax) value occurs. The coordinate is a 1-D array indexed by a local index having the name .Dim. The elements of the local index are the index names, and the value of the result is the element from that index.

By default, ArgMax and ArgMin return the index element(s). A positional dual is provided, in which the index position (rather than index element) is returned. For example:

ArgMax(R: Profit, I: UnitPrice, position: true)

returns the 1-based position in I, rather than the actual price. If an index might contain duplicate values, it may be necessary to use positions rather than elements to avoid ambiguities.

CondMin, CondMax

CondMin(X, cond, I)
CondMax(X, cond, I)

CondMin returns the smallest value in «X» along «I» in which the corresponding condition, «cond», is not false. If «cond» is false everywhere along «I», returns -INF.

CondMax returns the largest value in «X» along «I» in which the corresponding condition, «cond», is not false. If «cond» is false everywhere along «I», returns INF.

Area

The full declaration for Area is:

Area(R: Numeric[J]; I: ascending Numeric[J]; X1, X2: opt numeric atomic; J: opt hidden IndexType = I)

With «J» not specified, «I» can be an index with numeric ascending values, or it can be a self-indexed array with arbitrary index values but with numeric ascending values along Self.

The «J» parameter also makes it possible to have an arbitrary X array:

Area(Y, X, J: In1)

where X and Y are arrays indexed by «J» (X must be ascending along «J»).

Some notes: «J» is hidden from the user, since the main concern was fixing several bugs with the function while maintaining backward compatibility. However, as the above example shows, it can be useful. The second parameter is named «I» for legacy reasons, but it would be better named «X» with the «J» parameter named «I». Consider this change when updating manuals.

Another difference relative to Analytica 3.1 is that «X2» (upper bound) can be specified while not specifying «X1» (lower bound). So, for example, you could find the area up to a given point, e.g.:

Var dens := Pdf(Ch1);
Area(dens, dens.Step, X2: x)