Iterate(initial, expr, until, maxIter, warn)
Repeats the computation «expr» until the termination condition «until» becomes true (nonzero), or the number of iterations reaches «maxIter». The parameters «expr» and «until» may, and usually do, depend on variable(s) that depends on the variable defined with Iterate. This creates a recurrence, cycle or loop in the influence diagram, something that is not otherwise allowed except in Dynamic loops. Analytica iterates the evaluation around the cycle until it reaches the termination condition «until», usually a convergence or equilibrium.
- «initial»: the starting value for the iteration
- «expr»: how the subsequent values are computed from the previous value. «expr» may, and usually does, depend directly or indirectly upon the variable defined by Iterate, forming a recurrence or cycle in the influence diagram.
- «until»: The termination condition. Iterate continues evaluating «expr» until «until» is true (non-zero) -- if «until» is an array, until all its elements are true (nonzero) -- or until the number of iterations reaches «maxIter», if specif.
- «maxIter»: (optional) The maximum number of iterations. If omitted, it will keep iterating until the «until» becomes true. Unless you are absolutely certain the iteration will converge, it is a good idea to specify a value for «maxinter».
- «warn»: (optional) If true (nonzero), it will give a warning if it terminates due to reaching the maximum number of iterations «maxinter», rather than the termination condition «until».
Iterate is useful when you want adjust a variable and repeat a calculation over several variables in the model to meet a constraint. If you can perform the iteration within the definition of a single variable or User-Defined Functions, it is usually simpler and clearer to use the While..Do construct.
For more complex cases, especially when you want to adjust multiple variables to solve constraints and/or find a maximum or minimum, it is simpler and faster to use the Analytica Optimizer.
This example models how a market price reaches equilibrium to correct an imbalance between supply and demand. It forecasts a the supply and demand given an initial price, and modifies the
Price iteratively until the supply exactly matches the demand reaching a market equilibrium.
Price is defined as:
Price := Iterate(Price_Guess, Next_price, Abs(Next_price-price) < 0.01, 50)
It iterates until the change in price between successive iterations is less than 0.01, or reaches 50 iterations.
Iterating Multiple Variables
You may want to update several variables in each iteration: For example, both
Market_size. You should define only one variable using Iterate. (If you define both
Market_size using Iterate, it will create two nested iterations, which would not do what you want.) Instead, you can define a new single variable that groups all the variables into a single state, listing them along an index.
In the above diagram,
State_index is defined as a list of labels:
State_index ▼ "Price" "Market Size"
You define the initial state and next state each as a table indexed by
Variable Initial_state := Table(State_index)(Price_Guess, Market_size_guess)
Variable Next_State := Table(State_Index)(Price_Guess, Market_size_guess)
State using Iterate:
Variable State := Iterate(Initial_state, Next_State, abs(state-next_state) < 0.01, 50)
For convenience in the rest of the model, it is useful to define
Market_Size as the elements of
Variable Price := State[State_Index = 'Price']
Variable Market_Size := State[State_Index = 'Market Size']
So far, we assume that
Market_size have the same dimensions -- both are scalars, or both are arrays with the same indexes. If you want them to have different dimensions, you can include their values using references:
Variable Initial_state :=
Variable Next_state :=
Variable Price :=
#State[State_index = 'Price']
Variable Market_size :=
#State[State_index = 'Market Size']
Variable State :=
Abs(Price-next_price) < 0.01 AND Abs(Market_size - Next_market_size) < 1,
Iterate with Dynamic
Iterate can be used in conjunction with Dynamic. Depending on the model structure, this can either result in an iteration within a dynamic loop, where the iteration to convergence occurs at each point in time, or it can result in a dynamic loop within an iteration, in which case the entire dynamic loop is computed to completion within each iteration.
X is a variable defined with Iterate, and
Y is a variable defined with Dynamic. Then
X might depend directly or indirectly on
Y might depend directly or indirectly on
X. This creates four distinct dependency structures. When neither
Y depends on the other, then there is no interaction. Similarly, when
Y depends on
X is not downstream of
X is not within
Y's dynamic loop, so again there is no interaction. In this case,
X is iterated to completion once, independent of the dynamic loop, then the dynamic loop performs its looping using this value.
X depends on
Y does not depend on
X, then the dynamic loop is completely inside the Iteration for
X, so that the dynamic loop is computed to completion within each iteration.
The most difficult case is when
Y both depend on the other. When this happens, the iterate expression could, in principle, be interpreted as being inside the dynamic or around the dynamic. Analytica resolves this ambiguity by assuming that the iterate is inside the dynamic, so that as the dynamic loop is evaluated at each time period, the iteration in
X is run to completion for that time period. Variables inside
X's definition are implicitly assumed to be sliced by the current dynamic time (as occurs with any variable appearing inside a dynamic loop).
You may encounter situations where you want to compute one or more variables using Iterate, which in turn are utilized within a dynamic loop, but where you desire the iteration to occur around the dynamic loop. For example, you may have completed an entire Dynamic model, and now you want to wrap Iterate around the full model to adjust an input parameter. In the future, we may introduce a new variation on the iterate function that would make it possible to specify that Iterate is wrapped around a given dynamic loop. Until then, one way to cause Iterate to wrap around a dynamic loop is by using a WhatIf to compute the value of the input parameter. To demonstrate, suppose you have an input parameter,
K, used by a dynamic model having some output
Y. We want to define
K using Iterate where the update expression and termination expressions depend on
Y. To do this, we introduce two variables:
Variable Y2 := WhatIf(Y, K, K2)
Variable K2 := Iterate(K, f(K2, Y2), g(K2, Y2))
f() is the update expression and
g() is the termination expression. When
K2 is evaluated, the iteration re-evaluates the full model repeatedly, with
K2 eventually being set to the "converged" value for the parameter
K. As a final step, we may want to set
K to be this computed value for
K2. For this last step, we would need to use a button:
Button Update_K := (K := K2)
Pressing the button causes the entire evaluation to take place.
(To do: provide some examples)
Interacting Iterate Functions
If you have two (or more) variables defined using the Iterate function, they should not be mutually dependent on each other. This case creates an ambiguity (for example, the result would depend on which was evaluated first), and results in an error message.
It is okay for one to be nested within the other, in which case the inner iteration runs to convergence during each iteration of the outer iterate. However, the need for nested iterations like this are rare. You should make sure that you don't intend to have a single iteration with multiple variables updated in the same iteration (see the earlier section on this page).
If you have two interacting Iterate loops that are not clearly nested, you will see this error message:
- A disallowed cyclic dependency involving the Iterate function in ident detected. You may have two nodes defined with Iterate functions, with each depending on the other. If you use two Iterate functions, one needs to be clearly nested inside the other. If you intend to have a single iteration involving two or more nodes, the Iterate function should appear only once.
Iterate with Change in Evaluation Mode
X is defined using Iterate, and the update expression depends on Sample(X) -- for example, it may use a statistical function like Mean(X). If
X is evaluated in Mid mode, it appears as if there are two interacting Iterate loops, and the above error message, A disallowed cyclic dependency involving Iterate.... occurs.
Iterate vs. While..Do
Iterate and While..Do are closely related, and both are often used for implementing convergence algorithms. When implementing an iterative algorithm in a User-Defined Function, you would normally use While..Do. However, in some situations you may develop a fairly sizable influence diagram model, where you wish to iterate over the entire model. In that case, Iterate is very convenient, and may require only small modifications.
The While..Do construct requires its parameter, which determines when the loop terminates, to be atomic, while Iterate allows array-valued termination conditions, but continues until all cells are true.
There are a few other approaches to implementing iterations within Analytica. NlpDefine performs a full optimization search. The Solve and GoalSeek functions are similar, although scaled down somewhat in their sophistication. User-Defined Functions can be recursive, although the recursion depth is usually limited to 255, which can be a limiting factor. And to iterate over an existing model structure, While..Do can be used with WhatIf, or from a button script, While..Do can be used with assignment (often a useful way to iterate in very large scale sampling applications that exceed memory required for built-in Monte Carlo sampling). And of course, there is Dynamic.