# Dataflow¶

*HPX* provides its users with several different tools to simply express parallel
concepts. One of these tools is a local control object (LCO)
called dataflow. An LCO is a type of component that can spawn a new
thread when triggered. They are also distinguished from other components by a
standard interface that allow users to understand and use them easily.
A Dataflow, being an LCO, is triggered when the values it depends on
become available. For instance, if you have a calculation X that depends on the
results of three other calculations, you could set up a dataflow that would begin
the calculation X as soon as the other three calculations have returned their
values. Dataflows are set up to depend on other dataflows. It is this property
that makes dataflow a powerful parallelization tool. If you understand the
dependencies of your calculation, you can devise a simple algorithm that sets
up a dependency tree to be executed. In this example, we calculate compound
interest. To calculate compound interest, one must calculate the interest made
in each compound period, and then add that interest back to the principal before
calculating the interest made in the next period. A practical person would, of
course, use the formula for compound interest:

where \(F\) is the future value, \(P\) is the principal value, \(i\) is the interest rate, and \(n\) is the number of compound periods.

However, for the sake of this example, we have chosen to manually calculate the future value by iterating:

and

## Setup¶

The source code for this example can be found here:
`interest_calculator.cpp`

.

To compile this program, go to your *HPX* build directory (see
HPX build system for information on configuring and building *HPX*) and
enter:

```
$ make examples.quickstart.interest_calculator
```

To run the program type:

```
$ ./bin/interest_calculator --principal 100 --rate 5 --cp 6 --time 36
Final amount: 134.01
Amount made: 34.0096
```

## Walkthrough¶

Let us begin with main. Here we can see that we again are using
Boost.Program Options to set our command line variables (see
Asynchronous execution with actions for more details). These options set the principal,
rate, compound period, and time. It is important to note that the units of time
for `cp`

and `time`

must be the same.

```
int main(int argc, char ** argv)
{
options_description cmdline("Usage: " HPX_APPLICATION_STRING " [options]");
cmdline.add_options()
("principal", value<double>()->default_value(1000), "The principal [$]")
("rate", value<double>()->default_value(7), "The interest rate [%]")
("cp", value<int>()->default_value(12), "The compound period [months]")
("time", value<int>()->default_value(12*30),
"The time money is invested [months]")
;
hpx::init_params init_args;
init_args.desc_cmdline = cmdline;
return hpx::init(argc, argv, init_args);
}
```

Next we look at hpx_main.

```
int hpx_main(variables_map & vm)
{
{
using hpx::dataflow;
using hpx::make_ready_future;
using hpx::shared_future;
using hpx::unwrapping;
hpx::naming::id_type here = hpx::find_here();
double init_principal=vm["principal"].as<double>(); //Initial principal
double init_rate=vm["rate"].as<double>(); //Interest rate
int cp=vm["cp"].as<int>(); //Length of a compound period
int t=vm["time"].as<int>(); //Length of time money is invested
init_rate/=100; //Rate is a % and must be converted
t/=cp; //Determine how many times to iterate interest calculation:
//How many full compound periods can fit in the time invested
// In non-dataflow terms the implemented algorithm would look like:
//
// int t = 5; // number of time periods to use
// double principal = init_principal;
// double rate = init_rate;
//
// for (int i = 0; i < t; ++i)
// {
// double interest = calc(principal, rate);
// principal = add(principal, interest);
// }
//
// Please note the similarity with the code below!
shared_future<double> principal = make_ready_future(init_principal);
shared_future<double> rate = make_ready_future(init_rate);
for (int i = 0; i < t; ++i)
{
shared_future<double> interest = dataflow(unwrapping(calc), principal, rate);
principal = dataflow(unwrapping(add), principal, interest);
}
// wait for the dataflow execution graph to be finished calculating our
// overall interest
double result = principal.get();
std::cout << "Final amount: " << result << std::endl;
std::cout << "Amount made: " << result-init_principal << std::endl;
}
return hpx::finalize();
}
```

Here we find our command line variables read in, the rate is converted from a
percent to a decimal, the number of calculation iterations is determined, and
then our shared_futures are set up. Notice that we first place our principal and
rate into shares futures by passing the variables `init_principal`

and
`init_rate`

using `hpx::make_ready_future`

.

In this way `hpx::shared_future`

`<double>`

`principal`

and `rate`

will be initialized to `init_principal`

and `init_rate`

when
`hpx::make_ready_future`

`<double>`

returns a future containing
those initial values. These shared futures then enter the for loop and are
passed to `interest`

. Next `principal`

and `interest`

are passed to the
reassignment of `principal`

using a `hpx::dataflow`

. A dataflow
will first wait for its arguments to be ready before launching any callbacks, so
`add`

in this case will not begin until both `principal`

and `interest`

are ready. This loop continues for each compound period that must be calculated.
To see how `interest`

and `principal`

are calculated in the loop, let us look
at `calc_action`

and `add_action`

:

```
// Calculate interest for one period
double calc(double principal, double rate)
{
return principal * rate;
}
///////////////////////////////////////////////////////////////////////////////
// Add the amount made to the principal
double add(double principal, double interest)
{
return principal + interest;
}
```

After the shared future dependencies have been defined in hpx_main, we see the following statement:

```
double result = principal.get();
```

This statement calls `hpx::future::get`

on the shared future
principal which had its value calculated by our for loop. The program will wait
here until the entire dataflow tree has been calculated and the value assigned
to result. The program then prints out the final value of the investment and the
amount of interest made by subtracting the final value of the investment from
the initial value of the investment.