While¶
This guide provides an overview and brief instructions on using While blocks to implement workflow loops based on configurable conditions.
Getting started¶
With the While block, you can perform iterative computations by exchanging data with other blocks in a loop. This block drives a loop whose body may consist of one or more blocks. It performs a condition check, sends request data to the loop body, thereby starting its execution, receives the response data from the loop body, and starts its execution again if the specified condition is met, otherwise the loop ends.
The block is configured by default in such a way that each iteration of the loop begins with a condition check. If the condition is not met at the very first iteration, the block ends without executing the loop body. Otherwise, the block receives the response data from the loop body and starts the next iteration depending on whether the specified condition is met for the received data. The loop ends if the condition for the received data is not met. The following diagram illustrates the operation of the block in the mode described above:
flowchart TB
begin([Begin]) --> condition{Condition<br>check}
condition --> |Condition is not met| finish([End loop])
body --> |Response<br>from loop body| condition
condition --> |Condition is met<br><br>Request to loop body| body(Loop body execution)
Sometimes the response data from the loop body is required to check the condition on the first iteration. Therefore, the block also provides for unconditional execution of the loop body at the first iteration, after which the condition is checked. The block starts the next iteration depending on whether the specified condition is met for the received data, similar to the previously described mode of operation. The following diagram illustrates the operation of the block with unconditional execution of the loop body at the first iteration:
flowchart TB
begin([Begin]) --> |Request to loop body| body1(Loop body execution)
body1 --> |Response from loop body| condition{Condition<br>check}
condition --> |Condition is not met| finish([End loop])
body --> |Response<br>from loop body| condition
condition --> |Condition is met<br><br>Request to loop body| body(Loop body execution)
To select the operating mode, use the Unconditionally run the first iteration toggle in the block configuration dialog. This toggle is off by default, which causes the block to operate in the first mode described above. Turn this toggle on if you need to ensure that the loop body is unconditionally executed on the first iteration.
Block variables and their ports¶
The data to be checked is passed to the condition using variables. Each variable is associated with four ports whose names are the same as the variable's name:
- The input port to set the initial value of the variable. The value from this port is assigned to the variable at the start of the block execution.
- The request port for passing the variable value to the loop body. The port emits this value at the beginning of the iteration.
- The response port to receive the response value from the loop body. This value is assigned to the variable at the end of the iteration.
- The output port to emit the variable's value at the end of the loop. The current value of the variable appears on this port at the end of the block execution.
Thus, the value of a variable can change at the following points in time:
- At the start of the block execution, the variable is assigned the value from its input port.
- At the end of each iteration of the loop, the variable is assigned the value
from its response port, if there is a link connected to that port.
Otherwise the variable keeps its initial value from the input port.
- Note the above does not apply to the built-in variable
index
: the block increments its value by one at the end of each iteration, and if its response port is connected, it is used to signal that the loop body has finished execution, regardless of the response value (see Built-in ports for details).
- Note the above does not apply to the built-in variable
The rest of the block operation time the current value of the variable remains unchanged. It is this value that is used when checking the condition.
The While block provides the built-in variable index
. Its request
port can be used to pass the current iteration number to the loop body.
You can create other variables to receive responses and check them
against the condition.
Creating variables¶
To check a loop response against the condition, you need to create a variable. Together with each variable, four ports of the same name are created: an input port, a request port, a response port, and an output port. The condition can be checked for the value received through the variable's input port (on the first iteration) or response port (on subsequent iterations).
The variables are listed in the Variables area of the block configuration dialog. To create a variable, click the button at the top of this area, and then input a name for the new variable and its initial value. The name identifies the variable in the condition expression and is also assigned to the ports associated with the variable. The initial value is assigned to the variable's input port.
Usage example¶
To clarify the operation of the While block, consider the following example.
Suppose that solving a certain problem requires several iterations - such as
successive approximations - performed by a block named Solver
, which has a
single input and a single output. The input requires the current iteration
number. The output sends either True
, indicating that the problem has been
solved at the current iteration, or False
, meaning that more iterations are
required.
With the While block you can implement the solving loop as follows:
- In the While block configuration dialog, create a variable named
solved
and set its initial value toFalse
. - Link the
index
request port of the While block to theSolver
input. - Link the
Solver
output to thesolved
response port of the While block. - Use the value of the
solved
variable in the condition. The solving loop continues while the condition is met, so the condition expression would benot "solved"
.
Since the block variables are assigned some initial values, their input ports
can be left unconnected, and the loop can be started by sending a signal
through a link to the input port @go
(see Signal ports).
At each iteration of the loop, the While block passes the current iteration
number to the Solver
block, causing it to start the next solving iteration.
After completing its iteration, the Solver
block returns either True
or
False
. If the While block receives False
, the condition expression
not "solved"
evaluates to True
(condition met), so the block outputs the
next iteration number. The loop continues until Solver
outputs True
.
Iteration number limit¶
In the above example, you can use the index
variable to limit the total
number of iterations. For instance, to set a limit of 100 iterations, add a
condition to check that the value of the index
variable is less than 100.
The condition expression in this case would be
not "solved" and "index"<100
. Since the index
variable value is
equal to the current iteration number, the loop is forced to end after the
100-th iteration.
Block setup basics¶
The fundamental elements of the While block setup are variables with their ports, and the condition expression:
- Built-in ports -
these are the ports of the
index
built-in variable, whose current value is the iteration number of the loop: theindex
input specifies the first iteration number, theindex
request port issues the number of the current iteration, theindex
response port receives a signal to start the next iteration, theindex
output issues the last iteration number when the loop ends. -
Added variable ports - these are the ports of the variables created in the block configuration dialog. They provide for setting the initial values of variables, passing and receiving data from the loop body, as well as issuing data received at the last iteration of the loop. Initial values and response data can be checked by adding respective variables to the condition expression.
With the added request and response ports, you can pass data for processing to the loop body, receive processed data, and then pass it for further processing in the next iteration, thereby implementing recurrent processing using the loop under the control of the While block. The added input and output ports allow you to set the initial data for processing and get the processing result at the end of the loop. For further details, see Recurrent processing.
-
Signal ports - these are two service ports of the same name: the
@go
input enables you to control the execution of this block, the@go
output signals that the execution of this block is completed. A link to the@go
input port ensures that the block is only executed when a value is received on that port. Linking to the@go
output port allows you to prevent the execution of another block until this block has completed its operation. - Setting up condition
requires composing a logical expression that evaluates to
True
orFalse
depending on the current values of variables. In the condition expression, variable values are represented by the names of the respective variables.
All block ports and their settings are displayed in the Block properties pane when you select the block in the workflow diagram. In the block configuration dialog, you can view or change the condition expression, create variables, and set the initial values of variables, which is the same as assigning values to variable input ports.
Built-in ports¶
To start running the While block, all required value data must be passed to
its input ports. The workflow can also control the startup of the While block
by signaling over a link to the @go
input port (see Signal ports).
On each iteration of the loop, the While block emits the values of the variables
to their request ports. The block has a built-in index
request port that can be
used to pass the current iteration number to the loop body. The number of the
first iteration is equal to the value on the index
input port (0 by default);
the number of each subsequent iteration is the number of the previous iteration
incremented by one.
Data can also be passed to the loop body through request ports of added variables
(see Added variable ports). If the request port of any
added variable is connected to the loop body, you may not connect the index
request port. The iteration counter operates within the While block regardless
of whether that port is connected.
Having sent request data, the block waits for response data to appear on its
response ports. The block has a built-in index
response port that serves to
signal the iteration completion from the loop body. To receive and check response
data against the block condition, use response ports of added variables.
The index
response port is normally connected if the block has no added
variables. The response value received on this port is disregarded and cannot
be checked against the block condition; it only signals that the execution
of the loop body within this iteration is completed.
Having received the response values, the While block can check them against
its condition. The exception is the index
response port: instead of the received
response value, the index
variable is assigned the subsequent iteration number.
If response values match the condition, the block issues values for the next iteration
through its request ports. In particular, the index
request port outputs the number
of the next iteration. The iteration ends with the receipt of response data from the
loop body through the response ports of the block variables.
At the end of the block execution, the index
output port issues the last iteration
number of the loop.
Implementing a for loop¶
You can use the While block to implement a loop with a given number of iterations - a for loop. Such a loop has a sequence of integers that define iterations, and a body that is executed at each iteration. A for loop is typically used when the number of iterations is known before entering the loop - such a loop is essentially a request to perform a certain sequence of actions a given number of times.
You can implement a for loop using the built-in index
ports:
- Connect the
index
request port to the input of the loop body. - Connect the output of the loop body to the
index
response port. - In the condition expression, specify that the value of the
index
variable should be less than the desired number of iterations, such as"index"<100
.
To pass the desired number of iterations to the block, you can create
a variable, such as max_iter
, and assign a value to that variable
through its input port. The for loop condition in this case would be
"index"<"max_iter"
.
Added variable ports¶
A basic function of the While block is to check responses against a given
condition. The block can receive response values via the built-in index
port, but the value thus obtained cannot be checked, as it is replaced by the
subsequent iteration number. To check responses and other input data,
variables are used that can be created in the While block configuration
dialog.
Each variable adds four ports of the same name: an input port, a request port, a response port, and an output port. The names of all these ports are the same as the names of the variables that are listed in the Variables area of the block configuration dialog. To create a variable, click the button at the top of this area, and then enter a name for the new variable. This name identifies the variable in the condition expression and is also assigned to the ports of the variable.
Added ports enable you to change the value of the variable and return its current value to the workflow: the value of the input port is assigned to the variable at the beginning of the block execution, the request port passes the current value of the variable to the loop body, the value of the response port is assigned to the variable at the end of the iteration, and the output port returns the current value of the variable at the end of the loop. The variable retains its value between iterations of the loop, allowing data to be processed recurrently, since the value of the variable passed through the request port in the subsequent iteration is the value received through the response port in the previous iteration.
Recurrent processing¶
Variables and their ports enable recurrent data processing, where at each subsequent iteration, the results of the previous iteration are passed to the loop body. A typical example of such processing is a method of successive approximations that sets some initial value and calculates each new value based on the previous one. To enable such a recurrent sequence of calculations, the ports of the variable are used in the following manner:
- Input port specifies the initial value of the recurrent sequence.
- Request port feeds the loop body with the value received at the previous iteration.
- Response port receives the value calculated at the current iteration.
- Output port issues the value received at the final iteration.
On the first iteration of the loop, the variable's request port passes the value received though its input port to the loop body, then the block receives the response values. On subsequent iterations, the variable's request port passes the value that the variable's response port received in the previous iteration. After checking the condition, the next iteration is started, or the loop ends. The value received on the variable's response port in the final iteration is issued through its output port.
Signal ports¶
The While block has two ports intended to control the execution of the
block - an input port named @go
and an output port of the same name.
The @go
input port enables the workflow to start the block using a
link to that port. The @go
output port emits a value indicating that
the block has completed execution.
With the @go
input port, you can delay the execution of the block until a
signal is sent to that port. The @go
output port allows you to pass an
execution trigger signal to the input of another block. Thus, the execution of
one block can be made dependent on the completion of the execution of another
block.
The @go
input port receives a signal indicating that the block can start
running. If this port is not connected anywhere, the block starts running as
soon as it receives all the necessary data. However, if you connect a link to
the @go
input port, the block will wait for a signal on that port. Such a
signal is any value received through a link from an output port of another
block.
The signal to run the block can be any value passed via a link to the @go
input port. This value only permits the execution and does not affect the
operation of the block in any way. When the block execution completes, the
@go
output port emits a value that can be passed, for example, via a link
with the @go
input port of another block.
Setting up condition¶
Condition is a logical expression that can include variable values. In the
condition expression, variable names in double quotes are used to represent
variable values. The condition is deemed met if its expression evaluates to
True
.
To specify a condition, enter the condition expression in the Condition area of the block configuration dialog.
Condition expressions allow the use of basic comparison operators, such as
"equals" ("a"=="b"
), "less than" ("a"<"b"
), "greater or equal"
("a">="b"
) and so on, as well as the and
, or
, and not
Boolean
operators. Arithmetic operators are also allowed. Parentheses allow you to
control the order in which the expression is evaluated.
When composing expressions, observe the following basic rules:
- Each variable name must be enclosed in double-quotes. For example,
the expression
"index"<100
checks the value of the variable namedindex
. The variable name is enclosed in double quotes:"index"
. -
If the variable value is an array, the element index must be outside the double quotes surrounding the name of the variable, for example:
"result"[2]<0
(list item),"result"[3][0]>1
(matrix element).Array indexing is zero-based, so for example
"result"[2]
is the third element of the array represented by the value of theresult
variable. -
If the variable value is a dictionary, the item key must be outside the double quotes, for example:
"res"['error']<=0.1
.
For more information on composing expressions, see Condition expressions.
Condition expressions¶
Conditions are defined by logical expressions that evaluate to True
or
False
. A condition is deemed met if its expression evaluates to True
.
Condition expressions must conform to the basic Python syntax for
expressions and may contain variables, literals, and operators.
The variable names in the condition expression are the names of the variables that are created in the block configuration dialog. When evaluating the expression, the name of the variable is replaced by its current value.
Strings and numbers can be used as literals. A literal string must be
enclosed in single quotes ('string'
) - you cannot use double quotes
because a double-quoted string is considered a variable identifier.
Compound values, such as lists, matrices, and dictionaries, can be
passed to the expression through variables. The creation of such values
within an expression is not allowed. For instance, a list literal
[1, 2, 3]
in an expression will cause an error when evaluating
the expression.
The expression evaluates successfully when data of the expected type
is substituted into it. If the current value of the variable does not
match the expected data type, evaluation of the expression may fail.
For instance, a numeric value of the variable "x"
results in an error
when evaluating the expression "x"[2]<0
because "x"
is expected
to be a list.
Operators¶
The following operators are allowed in condition expressions:
Comparison operators
==
- equal to, not to be confused with the=
assignment operator; when used with strings, the comparison is case-sensitive<
- less than>
- greater than<=
- less than or equal to>=
- greater than or equal to
Arithmetic operators
+
- addition-
- subtraction*
- multiplication/
- division%
- division modulo (remainder of division)
Logical operators
and
-True
if both operands areTrue
; otherwise,False
or
-True
if either operand isTrue
; otherwise,False
not
-True
if the operand isFalse
;False
if the operand isTrue
Membership test operator
in
-True
if the left operand is contained in the right one; with strings, works as a substring search
Examples¶
Some condition expressions are:
"continue" and "index"<100
- check if the value of thecontinue
variable isTrue
and the value of theindex
variable is less than 100."x">=0 and "x"<=1
- check if the numeric value of thex
variable is in the range from 0 to 1."r0"<"r1" and "r1"<"r2" and "r2"<"r3"
- check if the numeric values of the variablesr0
,r1
,r2
, andr3
are strictly in ascending order."f_error"/"f"<0.05
- assuming numeric values of thef_error
andf
variables, check if thef_error
tof
ratio is less than0.05
."func"[0]<"func"[1]
- assuming the value of thefunc
variable is a list of numbers, check if the first number in the list is less than the second number (note that list indexing is zero-based)."res"['error']>"threshold"
- check if the value of theerror
key in the dictionary represented by the value of theres
variable is greater than the value of thethreshold
variable.