# SetEvaluationFlag

## SetEvaluationFlag(flag, enable, expr)

Sets or resets a flag that has some effect on how Analytica evaluates expressions, and then evaluates «expr» with the indicated flag value. The change takes effect only within the scope of «expr» -- after the function completes, the flag has its original value. This is used primarily for turning on or off certain speed optimizations in cases where those speed optimizations might interact in an undesirable manner with «expr».

The «flag» parameter may be one of the following values:

• 'evaluationContext'
• 'multithreaded:«calculation»' (where «calculation» is one of the names listed below)
• 'sparse'

These are described in detail in the sections below.

To set a flag, pass True for the «enable» parameter. To clear a flag, pass False for «enable».

## EvaluationContext

Suppose only one slice of an intermediate result will be retained in the final result. Analytica can reduce computation time by only computing the one slice.

The system variable EnableEvalContextUse controls whether this speed optimization is applied globally to the whole model. It is set to 1 by default. SetEvaluationFlag is used to override this global setting just while «expr» is evaluated.

There are a few potential reasons for disabling this speed optimization. Usually it will be because you have a legacy model that was created prior to Analytica 4.6 (when this speed optimization was introduced), and you find that this optimization changes or breaks something in your model. Hence, you can turn the feature off to retain backward compatibility. You might turn it off globally initially, but then later turn it back on globally and use SetEvaluationFlag to turn it off only in specific places.

If a bug is discovered in the future (i.e., the optimization screws up something), SetEvaluationFlag will provide a way to work around it. Hopefully this won't happen.

Consider the expression, where A and B are both indexed by I, an index with 10 elements.

Var count := 0;
Var x[ ] := A;
Var y[ ] := B+x;
MsgBox(x & ',' & y);
count := count + 1;
Uniform(x, y)

The declaration for x specifies that it has no indexes, it is atomic, within the lexical context of x. Since A is indexed by I, this requires the expression that follows to be evaluated 10 times, once for each atomic value of A. The same holds for y. However, the evaluation context optimization allows Analytica to recognize that a second iteration over I is unnecessary. Hence, the body needs to be evaluated only 10 times. When this completes, count will be 10.

Without the evaluation context optimization, two nested iterations occur. You'll see a MsgBox 100 times, count will be 100 at the end, and Uniform will have been called 100 times. This illustrates three differences that the optimization can cause in legacy models: Side-effects in the form of interactions with the user may occur fewer times, side-effects in the form of variable assignments (count) may occur fewer times, and the number of calls to random number generation may occur fewer times, this altering the RandomSeed, which in turn will alter the random values sampled during Monte Carlo analysis.

Here is another example. Suppose x in indexed by I and J.

Function F(A: Array[I]; I: Index; y: atom)
F(x, I, J)[J = "profit"]

Evaluating F(x) by itself requires multiple calls to F, one for each value of J. But because only one slice of the result is used, the evaluation context optimization will cause F to be called only once. If your function F has side-effects -- writing data to a file or external database, showing the user message boxes, assigning to variables, etc. -- the optimization may be undesirable. You would remedy this using

SetEvaluationFlag("evaluationContext", false,
F(x, I, J)[J = "profit"]
)

And one other example. Suppose A is indexed by I.

Function F1(x: atom)
Function F2(x: atom) := F1(x + I)
F2(A)

Even when x is atomic, x + I is an array indexed by I, so F1(x + I) would normally require iteration. But when F2 in being evaluated on the A[I = n] slice of A, only the I = n slice of the result of F2 will be retained, so within F2, F1 can be called a single time, essentially F2(x + n). Again, the smaller number of calls might be an issue if F1 has side-effects.

This case can be addressed either from the calling expression, or from within F2's definition. From the calling expression, we would use

SetEvaluationFlag("evaluationContext", false, F2(A))

This illustrates that the flag applies to User-Defined Functions called directly from «expr», or nested within UDFs called from «expr».

(New to Analytica 5.2)

The options 'multithreaded:«calculation»' provide a way to selectively disallow the use of multithreaded evaluation during the evaluation of an expression. The motivation for this feature is to provide a way to work around bugs in multithreaded algorithms that might be discovered long after a release has gone out, without having to turn off multithreading entirely.

As an example, when DefineOptimization creates an array of optimization problems (i.e., when there is an extrinsic index), it is able to speed-up creation of problem instances by filling in the problem instances in parallel. Suppose that it is later discovered that one of the solver engines experiences an intermittent "internal solver error" from a race condition when instances are populated in parallel. This may be avoidable by turning off multithreading entirely, but then everything else in the model runs more slowly. But by instead using

SetEvaluationFlag('multithreaded:DefineOptimization', false, DefineOptimization(...)

you can avoid that hypothetical bug without slowing down the rest of the model.

When you use an 'multithreaded:...' option, you will generally be turning off the flag, setting «enable» to false, since these flags are usually on by default.

Each of these flags can be turned off for the entire scope of the model from the system variable DisableMultithreaded, without using SetEvaluationFlag. If a flag has been turned off globally there, then you could use SetEvaluationFlag to turn it on temporarily for a specific calculation with the «enable» parameter set to true.

The flag impact calculations directly in «expr», but not calculations in other variables that are referenced by «expr».

The possible calculations that can be specified are as follows:

• 'multithreaded:Aggregate': evaluation of the function Aggregate.
• 'multithreaded:Arithmetic': arithmetic operators (+, *, -, /, ^) on large arrays.
•  'multithreaded:Assignment':copying arrays during assignment with :=.
• 'multithreaded:Beta':evaluation of the Beta distribution function.
• 'multithreaded:Binomial':evaluation of the Binomial distribution function.
• 'multithreaded:Comparison':comparison operators (=, <>, >, >=, <, <=) on large arrays~
• 'multithreaded:DefineOptimization':Populating multiple optimization instances in parallel during DefineOptimization
• 'multithreaded:DetermineCellTypes':An numerous points during calculations and in the UI, the engine needs to determine which data types are and are not present in an array. When doing so, it can walk large arrays in parallel.
• 'multithreaded:FunctionIteration':When the call to a thread-safe built-in function is iterated by array abstraction, the separate calls may be computed on separate threads. A different flag governs the more trivial scalar functions (such as Abs(x) that involves only scalars), whereas this one involves functions that might get passed arrays.
• 'multithreaded:Gamma:evaluation of the [[Gamma] distribution function.
• 'multithreaded:If':After a, b and c are computed, this flag impacts the algorithm that merges these into the final result, for If, IfAll and IfOnly.
• 'multithreaded:IsEq':Internal operations when Analytica needs to test whether two arrays' contents are equal.
• 'multithreaded:LogNormal':evaluation of the LogNormal distribution function.
• 'multithreaded:MatrixMultiply':evaluation of the MatrixMultiply function.
• 'multithreaded:NegBinomial':evaluation of the NegBinomial distribution function.
• 'multithreaded:Normal':evaluation of the Normal distribution function.
• 'multithreaded:Reform':The internal algorithm that transforms results into canonical order.
• 'multithreaded:ScalarFunctions':When scalar functions (usually very simple functions, involving scalar inputs and outputs and no side effects, such as Sqrt, Abs, Sin, etc., are iterated over arrays.
• 'multithreaded:Sequence':Evaluation of the Sequence function.
• 'multithreaded:SliceSubscript'|Evaluation of Slice and Subscript operations, including the functions, the operators, and internal use of slicing and subscripting by other functions and UI.
• 'multithreaded:Sort':The sort performed within SortIndex, Sort, Rank, RankCorrel, GetFract (sometimes), as well as other internal algorithms and UI code that uses sorts.
• 'multithreaded:SumMax':evaluation of function Sum, Max, SetUnion, and SetIntersection.
• 'multithreaded:*':This option turns off or on all the above flags during evaluation of «expr».

## Sparse

(Experimental in Analytica 5.2)

As of Analytica 5.2, fully sparse arrays are an experimental feature. There is a real possibility of not-yet discovered bugs, including bugs that might result in incorrect computations, so use at your own risk.

This flag controls whether certain operations on non-sparse arrays may return a (fully) sparse result. For example, the result of I=I in the identity matrix, which is clearly quite sparse in theory. Without this flag, a dense array with $n^2$ entries is returned, whereas with this flag a sparse array with roughly $2n$ entries is returned (i.e., 0 as the default for each row, and the single 1 in each row). When this sparse array is used in other calculations involving sparse arrays, computation speed in some cases may be dramatically reduced, for example using $2n$ steps instead of $n^2$ steps. For very high-dimensional examples, the difference is sometimes astronomical (this experimental full sparsity applies to any number of dimensions).

Since release 2.0, Analytica has made use one form of sparsity, which we might call "constant sparsity". This form of sparsity occurs when all the slices of an array or subarray have the same value (is constant along an index). For example, the result of If I=1 then J else -1 is constant for all values along J whenever I<>1, so those are stored internally using a single value. The sparse flag discussed in this section has no impact on constant sparsity -- it only impacts whether full sparsity is introduced.

The most dramatic introduction of full sparsity may occur during an MdTable relational-to-array transformation. In this case there is also an optional parameter to MdTable. The Sparse evaluation flag essentially determines the default, which gets overridden for the MdTable call if the optional parameter is specified.

As of Analytica 5.2, where the use of sparse arrays is off by default, SetEvaluationFlag('sparse', true, «expr» may be used to enable it for the operations that occur within «expr». This flag does not apply to expressions in other variables of user-defined functions that may referenced within «expr». If this feature is transitioned to a non-experimental feature in a future release, then SetEvaluationFlag will be utilized to turn it off for operations in «expr». It is tough to know when the introduction of full sparsity will perform better that dense arrays. For arrays that are intrinsically dense, the dense representation will perform better for both time and space. In some cases, even a small amount of sparsity can turn into speedup, whereas in others a large sparsity is required. When this flag is on, the internal algorithms must use heuristics to determine whether they should introduce sparsity. If you know that a dense representation is superior, turning sparse off will be useful for forcing the use of dense arrays.

## History

This function was introduced in Analytica 4.6.

The 'multithreaded:' and 'sparse' options are new to Analytica 5.2.

Two options, 'FastCOMcalls' and 'FastCOMproperties' had existed for a short time during Analytica 4.6 beta testing, but were rendered obsolete by an improved algorithm.