# If, while and branching

Here we will introduce how if statements and while loops are represented in TaskBlaster by making use of branching. Since this is an advanced topic it is suggested that you first read about the conceptual difference between static and dynamical workflows in Dynamical workflows.

## A single if-statement

Consider following piece of pseudo code:

```
a = A()
if B(a):
c = C(a)
else:
d = D(a)
```

This may seem simple enough, but when it comes to workflows it is actually quite complex. If A, B, C and D are *tasks* then depending on the outcome
of task B either C or D will be executed. There will never be a case where both task C and D are executed and it is not possible to know which task that
should be executed before A and B have been evaluated. Therefore, this code has to be represented by a dynamical workflow.

Let us now try to create the workflow above in TaskBlaster step by step. First initialize a repository, and write dummy tasks to tasks.py to represent tasks A, B, C, and D. For convenience, we also include task E for further tutorial in this page.

```
$ tb init
Created repository using module "taskblaster.repository" in "/home/myuser/tmprepo".
```

```
def A():
return 1
def B(a):
return a > 1
def C(a):
return a + 2
def D(a):
return a + 3
def E(a):
return a + 4
```

And write the workflow corresponding to the pseudocode above. Note that at this stage of the tutorial, the exact Taskblaster syntax is irrelevant (it will be revisited below in more detail). Crucial thing here is that this is the exact Taskblaster representation of the pseudo code (for a graphical representation see Dynamical workflows).

```
import taskblaster as tb
@tb.workflow
class DynamicWorkflow:
@tb.task
def A(self):
return tb.node('A')
@tb._if(true='Cbranch', false='Dbranch')
@tb.task
def B(self):
return tb.node('B', a=self.A)
@tb.branch('Cbranch')
@tb.task
def C(self):
return tb.node('C', a=self.A)
@tb.branch('Dbranch')
@tb.task
def D(self):
return tb.node('D', a=self.A)
def workflow(runner):
runner.run_workflow(DynamicWorkflow())
```

We have introduced two new decorators to tasks. Firstly, the `@tb.branch(<branch name>)`

decorator, which will assign a task
to a particular branch. If the decorator is omitted, the default branch will be `èntry`

.

Secondly, we utilized the `@tb._if(true=<if branch>, false=<else branch>)`

decorator. The task with this decorator
will now return a result, and whether that evaluates to True or False, a particular branch is chosen. Note the underscore
in `_if`

because `if`

cannot be used since it is a reserved word. Same applies to lower case `true`

and `false`

parameters.

We can now execute the workflow:

$ tb workflow workflow.py entry: add new 0/0 tree/A if: add new 0/1 tree/B T=Cbranch F=Dbranch

We see that only `A`

and `B`

tasks were created. We see this `entry:`

text, which
is the default branch for each task, if they are not given any other branch name
by the `@tb.branch`

decorator.

We also see that B has some extra markings
on it, `if`

on the left hand side, and information about the control flow on
the right hand side. The light blue color of the True and False results
indicate that this branch has not been decided yet (because the `B`

“if-task”
is not yet done).

We can observe the current status of the project:

$ tb ls state deps tags worker time folder ─────────────────────────────────────────────────────────────────────────────── new 0/0 tree/A new 0/1 tree/B

The above relates us back to the initial paragraph of the static workflow.
The task dependency graph has now been generated as far as it can, before it
encountered a branching statement. This is as far as static workflows can
get us, thus *to execute one branch, is to execute a static workflow*.

We can now run and observe the workflow:

```
$ tb run .
Starting worker rank=000 size=001
[rank=000 2024-08-19 13:00:17 N/A-0/1] Worker class: —
[rank=000 2024-08-19 13:00:17 N/A-0/1] Required tags: —
[rank=000 2024-08-19 13:00:17 N/A-0/1] Supported tags: —
[rank=000 2024-08-19 13:00:17 N/A-0/1] Main loop
Got task <taskblaster.worker.LoadedTask object at 0x7f52cb0576b0>
[rank=000 2024-08-19 13:00:17 N/A-0/1] Running A ...
[rank=000 2024-08-19 13:00:17 N/A-0/1] Task A finished in 0:00:00.001440
Got task <taskblaster.worker.LoadedTask object at 0x7f52ca69cd10>
[rank=000 2024-08-19 13:00:17 N/A-0/1] Running B ...
[rank=000 2024-08-19 13:00:17 N/A-0/1] Task B finished in 0:00:00.000588
[rank=000 2024-08-19 13:00:17 N/A-0/1] No available tasks, end worker main loop
```

$ tb ls state deps tags worker time folder ─────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/A done 1/1 N/A-0/1 00:00:00 tree/B

We note that the task to determine the branching is now ready, so we may continue with the workflow execution.

$ tb workflow workflow.py entry: have done 0/0 tree/A if: have done 1/1 tree/B T=Cbranch F=Dbranch jump: Dbranch Dbranch: add new 2/2 tree/D

The workflow command will discover that the task associated with the if statement
has now been evaluated, and it will make a jump. Therefore, it will execute
the new branch called Dbranch, which is highlighted by making it green,
and the rejected branch was made red. We also see, that due to creation of the
Dbranch (in similar static sense as the entry-branch above) we obtain one
new task, namely `D`

.

We can now run this `D`

task, to complete the workflow.

```
$ tb run .
Starting worker rank=000 size=001
[rank=000 2024-08-19 13:00:18 N/A-0/1] Worker class: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Required tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Supported tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Main loop
Got task <taskblaster.worker.LoadedTask object at 0x7f9acc7876b0>
[rank=000 2024-08-19 13:00:18 N/A-0/1] Running D ...
[rank=000 2024-08-19 13:00:18 N/A-0/1] Task D finished in 0:00:00.001319
[rank=000 2024-08-19 13:00:18 N/A-0/1] No available tasks, end worker main loop
```

Running the workflow command has no further effect.

$ tb workflow workflow.py entry: have done 0/0 tree/A if: have done 1/1 tree/B T=Cbranch F=Dbranch jump: Dbranch Dbranch: have done 2/2 tree/D

as all of the tasks are done.

$ tb ls state deps tags worker time folder ─────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/A done 1/1 N/A-0/1 00:00:00 tree/B done 2/2 N/A-0/1 00:00:00 tree/D

## Converging from multiple branches

Very often, we would like to take a detour with branching, and then return to the master control flow. In the following we will use the Collatz sequence as an example. The Collatz sequence is generated, starting from any positive number and following two simple rules:

If the number is even, divide by two

If the number is odd, multiply by three and add 1.

The Collatz conjecture states that the sequence will always lead to the number 1.

We will now recreate one iteration of the Collatz sequence (in the next section, we will iterate this further).

Let’s define very simple tasks.

```
def is_even(number):
return number % 2 == 0
def divide_by_two(number):
return number // 2
def three_n_plus_one(number):
return 3 * number + 1
def results(number, sequence):
return number, sequence + [number]
def sequence(sequence, number):
return [*sequence, number]
def stop_iteration(number):
return number == 1
```

The pseudo code to do one iteration can be written as:

```
def CollatzIteration(number, sequence):
if is_even(number):
new_number = divide_by_two(number)
else:
new_number = three_n_plus_one(number)
return results(new_number, sequence)
```

Let us write this workflow with Taskblaster. Following the same basic steps, we initialize the repository.

```
$ tb init
Created repository using module "taskblaster.repository" in "/home/myuser/tmprepo".
```

Write the workflow for one iteration of the Collatz to the file
`converging_workflow.py`

:

```
import taskblaster as tb
@tb.workflow
class CollatzIteration:
number = tb.var()
sequence = tb.var()
@tb._if(true='even_branch', false='odd_branch')
@tb.task
def IsEven(self):
return tb.node('is_even', number=self.number)
@tb.branch('even_branch')
@tb.jump('results')
@tb.task
def even_task(self):
return tb.node('divide_by_two', number=self.number)
@tb.branch('odd_branch')
@tb.jump('results')
@tb.task
def odd_task(self):
return tb.node('three_n_plus_one', number=self.number)
@tb.branch('results')
@tb.task
def gather_result(self):
return tb.node(
'results',
number=tb.Phi(
even_branch=self.even_task, odd_branch=self.odd_task
),
sequence=self.sequence,
)
def workflow(runner):
runner.run_workflow(CollatzIteration(number=5, sequence=[]))
```

We have introduced a new `@tb.jump(<branch>)`

keyword, which causes control flow to
jump immediately from current branch to another.
The usecases of jump include returning to main control flow after
diverging into multiple branches.

Note

One is allowed to have exactly one branching statement inside a branch, that is only
one if or jump is allowed per branch. Below, is an illustration of `valid`

branch
and an `invalid`

branch with two branching statement.

```
@tb.branch('valid')
@tb.jump('target')
def taskA(self):
...
@tb.branch('valid')
@tb.jump('target')
def taskB(self):
...
@tb.branch('invalid')
@tb.jump('target')
def taskC(self):
...
@tb.branch('invalid')
@tb.if(true='target', false='otherbranch')
def taskD(self):
...
```

We have also introduced a new keyword, `tb.Phi(<branchname>=<self.task_name>, ...)`

,
which is used to select the correct branch for children Tasks with
branching parent Tasks. `tb.Phi`

provides a mechanism to reference data
before it exists, and resolve these inputs once the task has been completed,
passing the correct branch to the children tasks.

That is, when entering a branch with a task with a Phi-operator, the rule is that
it will select the input routing based on which branch it came from. In this case,
that is triggered by the jump statements. For the explicit example above the `gather_result`

task will thus
execute the function `results`

with the input argument *number* given by the output from the `èven_task`

if the *even_branch* was executed or by the output from the `odd_task`

if the *odd_branch* was executed.

Now run the workflow

$ tb workflow converging_workflow.py entry:if: add new 0/0 tree/IsEven T=even_branch F=odd_branch

We can now run this task, and again, rerun the workflow to make a new iteration. We repeat these steps, until everything is done.

```
$ tb run .
Starting worker rank=000 size=001
[rank=000 2024-08-19 13:00:18 N/A-0/1] Worker class: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Required tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Supported tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Main loop
Got task <taskblaster.worker.LoadedTask object at 0x7f7454e98860>
[rank=000 2024-08-19 13:00:18 N/A-0/1] Running IsEven ...
[rank=000 2024-08-19 13:00:18 N/A-0/1] Task IsEven finished in 0:00:00.001433
[rank=000 2024-08-19 13:00:18 N/A-0/1] No available tasks, end worker main loop
```

$ tb workflow converging_workflow.py entry:if: have done 0/0 tree/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: add new 1/1 tree/odd_task jump: results results: add new 1/2 tree/gather_result

```
$ tb run .
Starting worker rank=000 size=001
[rank=000 2024-08-19 13:00:18 N/A-0/1] Worker class: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Required tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Supported tags: —
[rank=000 2024-08-19 13:00:18 N/A-0/1] Main loop
Got task <taskblaster.worker.LoadedTask object at 0x7f7737cfe240>
[rank=000 2024-08-19 13:00:18 N/A-0/1] Running odd_task ...
[rank=000 2024-08-19 13:00:18 N/A-0/1] Task odd_task finished in 0:00:00.001351
Got task <taskblaster.worker.LoadedTask object at 0x7f7737ce3f20>
[rank=000 2024-08-19 13:00:19 N/A-0/1] Running gather_result ...
[rank=000 2024-08-19 13:00:19 N/A-0/1] Task gather_result finished in 0:00:00.000658
[rank=000 2024-08-19 13:00:19 N/A-0/1] No available tasks, end worker main loop
```

$ tb ls --sort=topo state deps tags worker time folder ─────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/IsEven done 1/1 N/A-0/1 00:00:00 tree/odd_task done 2/2 N/A-0/1 00:00:01 tree/gather_result

Let’s see the output, which is 16 as expected, because input 5 is odd, we calculate 5*3+1=16.

```
$ tb view tree/gather_result
name: gather_result
location: /home/myuser/tmprepo/tree/gather_result
state: done
target: results(…)
wait for: 0 dependencies
depth: 2
parents:
IsEven
odd_task
input:
["results", {"__tb_implicit_remove__": [["IsEven", {"__tb_type__": "ref", "index": [], "name": "IsEven"}]], "number": {"__tb_type__": "ref", "index": [], "name": "odd_task"}, "sequence": []}]
output:
[16, [16]]
Run information:
Worker name: N/A-0/1
Start time: 2024-08-19 13:00:18
End time: 2024-08-19 13:00:19
Duration: 0:00:01
Error: None
No custom actions defined for this task.
```

We can illustrate the control flow and task parameter routing now with the following figure (Similar to Dynamical workflows the parameter routing is represented by the dashed green arrows and the control flow by the thick solid arrows).

Note how we in all cases jump to the last branch, which then collects the results. In the next section, we will go into even more complicated dynamical workflows, as we will finish the Collatz sequence. That is we will further use this dynamical workflow, as a dynamic subworkflow, on a dynamic workflow.

## Dynamical subworkflows inside dynamical workflows

This will be the grande finale of the tutorial. We will utilize the workflow created above, the branching workflow, inside a workflow which demonstrates a while loop.

First initialize a new repository

Then write the following workflow to the file *collatz2_workflow.py*:

```
import taskblaster as tb
@tb.workflow
class CollatzIteration:
number = tb.var()
@tb._if(true='even_branch', false='odd_branch')
@tb.task
def IsEven(self):
return tb.node('is_even', number=self.number)
@tb.branch('even_branch')
@tb.jump('result')
@tb.task
def even_task(self):
return tb.node('divide_by_two', number=self.number)
@tb.branch('odd_branch')
@tb.jump('result')
@tb.task
def odd_task(self):
return tb.node('three_n_plus_one', number=self.number)
@tb.branch('result')
@tb.fixedpoint
@tb.task
def result(self):
return tb.node(
'define',
obj=tb.Phi(even_branch=self.even_task, odd_branch=self.odd_task),
)
@tb.workflow
class CollatzSequence:
number = tb.var()
@tb.branch('entry', loop=True)
@tb.subworkflow
def iteration(self):
return CollatzIteration(
number=tb.Phi(default=self.number, entry=self.iteration.result)
)
@tb.branch('entry', loop=True)
@tb.task
def sequence(self):
return tb.node(
'sequence',
sequence=tb.Phi(default=[self.number], entry=self.sequence),
number=self.iteration.result,
)
@tb.branch('entry', loop=True)
@tb._if(true='finish', false='entry')
@tb.task
def stop_iteration(self):
return tb.node('stop_iteration', number=self.iteration.result)
@tb.branch('finish')
@tb.task
def result(self):
return tb.node('define', obj=self.sequence)
def workflow(runner):
runner.run_workflow(CollatzSequence(number=5))
```

As you can see the main workflow `CollatzSequence`

now includes the `CollatzIteration`

as a subworkflow.

Note the keyword `loop`

to the branch decorator in `CollatzSequence`

. This keyword signifies that the branch can be revisited multiple times
which is the way you make while loops in TaskBlaster. In the workflow `CollatzSequence`

above first a single CollatzIteration will be executed.
The task `sequence`

adds the *number* to the list *sequence*. The first time the branch is visited the initial list sequence will just be the
input argument, the next time the branch is visited *number* will be added to the previously computed sequence. This is controlled by
`sequence=tb.Phi(default=[self.number], entry=self.sequence)`

. Finally the task `stop_iteration`

checks if *number=1*, if so we are finished and the
result (*sequence*) is collected by the task `result`

, otherwise the *entry* branch is revisited for a new iteration.

Let’s see what happens when we execute this workflow:

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry: add new 0/? tree/iteration-001/result if: add new 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch add new 0/1 tree/sequence-001 if: add new 0/1 tree/stop_iteration-001 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False new 0/? tree/iteration-001/result ø new 0/1 tree/sequence-001 ø new 0/1 tree/stop_iteration-001 ø

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: add new 1/1 tree/iteration-001/odd_task jump: result result: update new 1/2 tree/iteration-001/result update new 0/1 tree/sequence-001 if: update new 0/1 tree/stop_iteration-001 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry: add new 0/? tree/iteration-002/result if: add new 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch add new 2/3 tree/sequence-002 if: add new 1/2 tree/stop_iteration-002 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False new 0/? tree/iteration-002/result ø new 2/3 tree/sequence-002 ø new 1/2 tree/stop_iteration-002 ø

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: add new 2/2 tree/iteration-002/even_task jump: result result: update new 1/2 tree/iteration-002/result have new 2/3 tree/sequence-002 if: have new 1/2 tree/stop_iteration-002 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry: add new 0/? tree/iteration-003/result if: add new 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch add new 3/4 tree/sequence-003 if: add new 2/3 tree/stop_iteration-003 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True new 0/? tree/iteration-003/result ø done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False new 3/4 tree/sequence-003 ø new 2/3 tree/stop_iteration-003 ø

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: add new 2/2 tree/iteration-003/even_task jump: result result: update new 1/2 tree/iteration-003/result have new 3/4 tree/sequence-003 if: have new 2/3 tree/stop_iteration-003 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-003/even_task jump: result result: have done 2/2 tree/iteration-003/result have done 4/4 tree/sequence-003 if: have done 3/3 tree/stop_iteration-003 T=finish F=entry jump: entry entry: entry: add new 0/? tree/iteration-004/result if: add new 1/1 tree/iteration-004/IsEven T=even_branch F=odd_branch add new 4/5 tree/sequence-004 if: add new 3/4 tree/stop_iteration-004 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 1/1 N/A-0/1 00:00:00 tree/iteration-004/IsEven True new 0/? tree/iteration-004/result ø done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False new 4/5 tree/sequence-004 ø new 3/4 tree/stop_iteration-004 ø

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-003/even_task jump: result result: have done 2/2 tree/iteration-003/result have done 4/4 tree/sequence-003 if: have done 3/3 tree/stop_iteration-003 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-004/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: add new 2/2 tree/iteration-004/even_task jump: result result: update new 1/2 tree/iteration-004/result have new 4/5 tree/sequence-004 if: have new 3/4 tree/stop_iteration-004 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 1/1 N/A-0/1 00:00:00 tree/iteration-004/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-004/even_task 2 done 2/2 N/A-0/1 00:00:00 tree/iteration-004/result 2 done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False done 5/5 N/A-0/1 00:00:00 tree/sequence-004 [5, 16, 8, 4, 2] done 4/4 N/A-0/1 00:00:00 tree/stop_iteration-004 False

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-003/even_task jump: result result: have done 2/2 tree/iteration-003/result have done 4/4 tree/sequence-003 if: have done 3/3 tree/stop_iteration-003 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-004/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-004/even_task jump: result result: have done 2/2 tree/iteration-004/result have done 5/5 tree/sequence-004 if: have done 4/4 tree/stop_iteration-004 T=finish F=entry jump: entry entry: entry: add new 0/? tree/iteration-005/result if: add new 1/1 tree/iteration-005/IsEven T=even_branch F=odd_branch add new 5/6 tree/sequence-005 if: add new 4/5 tree/stop_iteration-005 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 1/1 N/A-0/1 00:00:00 tree/iteration-004/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-004/even_task 2 done 2/2 N/A-0/1 00:00:00 tree/iteration-004/result 2 done 1/1 N/A-0/1 00:00:00 tree/iteration-005/IsEven True new 0/? tree/iteration-005/result ø done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False done 5/5 N/A-0/1 00:00:00 tree/sequence-004 [5, 16, 8, 4, 2] done 4/4 N/A-0/1 00:00:00 tree/stop_iteration-004 False new 5/6 tree/sequence-005 ø new 4/5 tree/stop_iteration-005 ø

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-003/even_task jump: result result: have done 2/2 tree/iteration-003/result have done 4/4 tree/sequence-003 if: have done 3/3 tree/stop_iteration-003 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-004/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-004/even_task jump: result result: have done 2/2 tree/iteration-004/result have done 5/5 tree/sequence-004 if: have done 4/4 tree/stop_iteration-004 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-005/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: add new 2/2 tree/iteration-005/even_task jump: result result: update new 1/2 tree/iteration-005/result have new 5/6 tree/sequence-005 if: have new 4/5 tree/stop_iteration-005 T=finish F=entry state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 1/1 N/A-0/1 00:00:00 tree/iteration-004/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-004/even_task 2 done 2/2 N/A-0/1 00:00:00 tree/iteration-004/result 2 done 1/1 N/A-0/1 00:00:00 tree/iteration-005/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-005/even_task 1 done 2/2 N/A-0/1 00:00:00 tree/iteration-005/result 1 done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False done 5/5 N/A-0/1 00:00:00 tree/sequence-004 [5, 16, 8, 4, 2] done 4/4 N/A-0/1 00:00:00 tree/stop_iteration-004 False done 6/6 N/A-0/1 00:00:00 tree/sequence-005 [5, 16, 8, 4, 2, 1] done 5/5 N/A-0/1 00:00:00 tree/stop_iteration-005 True

$ tb workflow collatz2_workflow.py && tb run . > /dev/null && tb ls -csirITfo --sort=topo entry: entry:if: have done 0/0 tree/iteration-001/IsEven T=even_branch F=odd_branch jump: odd_branch odd_branch: have done 1/1 tree/iteration-001/odd_task jump: result result: have done 2/2 tree/iteration-001/result have done 1/1 tree/sequence-001 if: have done 1/1 tree/stop_iteration-001 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-002/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-002/even_task jump: result result: have done 2/2 tree/iteration-002/result have done 3/3 tree/sequence-002 if: have done 2/2 tree/stop_iteration-002 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-003/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-003/even_task jump: result result: have done 2/2 tree/iteration-003/result have done 4/4 tree/sequence-003 if: have done 3/3 tree/stop_iteration-003 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-004/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-004/even_task jump: result result: have done 2/2 tree/iteration-004/result have done 5/5 tree/sequence-004 if: have done 4/4 tree/stop_iteration-004 T=finish F=entry jump: entry entry: entry:if: have done 1/1 tree/iteration-005/IsEven T=even_branch F=odd_branch jump: even_branch even_branch: have done 2/2 tree/iteration-005/even_task jump: result result: have done 2/2 tree/iteration-005/result have done 6/6 tree/sequence-005 if: have done 5/5 tree/stop_iteration-005 T=finish F=entry jump: finish finish: add new 6/6 tree/result state deps tags worker time folder output ──────────────────────────────────────────────────────────────────────────────────────────────────────── done 0/0 N/A-0/1 00:00:00 tree/iteration-001/IsEven False done 1/1 N/A-0/1 00:00:00 tree/iteration-001/odd_task 16 done 2/2 N/A-0/1 00:00:00 tree/iteration-001/result 16 done 1/1 N/A-0/1 00:00:00 tree/iteration-002/IsEven True done 1/1 N/A-0/1 00:00:00 tree/sequence-001 [5, 16] done 1/1 N/A-0/1 00:00:00 tree/stop_iteration-001 False done 2/2 N/A-0/1 00:00:00 tree/iteration-002/even_task 8 done 2/2 N/A-0/1 00:00:00 tree/iteration-002/result 8 done 1/1 N/A-0/1 00:00:00 tree/iteration-003/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-003/even_task 4 done 2/2 N/A-0/1 00:00:00 tree/iteration-003/result 4 done 1/1 N/A-0/1 00:00:00 tree/iteration-004/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-004/even_task 2 done 2/2 N/A-0/1 00:00:00 tree/iteration-004/result 2 done 1/1 N/A-0/1 00:00:00 tree/iteration-005/IsEven True done 2/2 N/A-0/1 00:00:00 tree/iteration-005/even_task 1 done 2/2 N/A-0/1 00:00:00 tree/iteration-005/result 1 done 3/3 N/A-0/1 00:00:00 tree/sequence-002 [5, 16, 8] done 2/2 N/A-0/1 00:00:00 tree/stop_iteration-002 False done 4/4 N/A-0/1 00:00:00 tree/sequence-003 [5, 16, 8, 4] done 3/3 N/A-0/1 00:00:00 tree/stop_iteration-003 False done 5/5 N/A-0/1 00:00:00 tree/sequence-004 [5, 16, 8, 4, 2] done 4/4 N/A-0/1 00:00:00 tree/stop_iteration-004 False done 6/6 N/A-0/1 00:00:00 tree/sequence-005 [5, 16, 8, 4, 2, 1] done 5/5 N/A-0/1 00:00:00 tree/stop_iteration-005 True done 6/6 N/A-0/1 00:00:00 tree/result [5, 16, 8, 4, 2, 1]

Note how each iteration of the while loop is run seperately, since it involves resolving an if statement. The *loop*
keyword to the *branch* decorator automatically numbers the folders so that each iteration of the branch is given a specific folder. Finally the result
points to the sequence calculated in the final iteration.

```
$ tb view tree/result
name: result
location: /home/myuser/tmprepo/tree/result
state: done
target: define(…)
wait for: 0 dependencies
depth: 10004
parents:
sequence-005
stop_iteration-001
stop_iteration-002
stop_iteration-003
stop_iteration-004
stop_iteration-005
input:
["define", {"__tb_implicit_remove__": [["stop_iteration-005", {"__tb_type__": "ref", "index": [], "name": "stop_iteration-005"}], ["stop_iteration-004", {"__tb_type__": "ref", "index": [], "name": "stop_iteration-004"}], ["stop_iteration-003", {"__tb_type__": "ref", "index": [], "name": "stop_iteration-003"}], ["stop_iteration-002", {"__tb_type__": "ref", "index": [], "name": "stop_iteration-002"}], ["stop_iteration-001", {"__tb_type__": "ref", "index": [], "name": "stop_iteration-001"}]], "obj": {"__tb_type__": "ref", "index": [], "name": "sequence-005"}}]
output:
[5, 16, 8, 4, 2, 1]
Run information:
Worker name: N/A-0/1
Start time: 2024-08-19 13:00:23
End time: 2024-08-19 13:00:23
Duration: 0:00:00
Error: None
No custom actions defined for this task.
```

The graphical representation of the workflow is given `here`

.
Note, that due to experimental nature of the automatic workflow visualizer, some variable arrows are missing,
but the branching arrows are all there.