Skip to content

Commit 7ceb5f5

Browse files
author
Damian Rouson
authored
Merge pull request #54 from sourceryinstitute/rm-vertex-id
Use functional programming patterns
2 parents 293786e + dc8c5e8 commit 7ceb5f5

File tree

10 files changed

+338
-230
lines changed

10 files changed

+338
-230
lines changed

README.md

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,71 @@ Overview
99
========
1010

1111
DAG is a Fortran 2018 library for creating and manipulating directed acyclic graphs (DAGs).
12-
DAG is based on a fork of [daglib by Jacob Williams], refactored to add
12+
DAG is based on a fork of [daglib by Jacob Williams], refactored to
1313

14-
* A build system and test harness automated by [fpm],
15-
* Unit testing written with [Vegetables],
16-
* Continuous-integration testing and documentation deployment via [GitHub Actions],
17-
* Documentation generated by [FORD],
18-
* Runtime assertion-checking using [Assert], and
19-
* [JSON] file input/output using [jsonff].
14+
* Adopt a functional programming pattern based on (mostly) pure function constructors
15+
and component getters but no setters. When compilers cooperate, we recommend using
16+
`associate` to assign names to constructor results to avoid unnecessary copies.
17+
* Add build system and test harness automated by [fpm],
18+
* Add unit testing written with [Vegetables],
19+
* Add continuous-integration testing and documentation deployment via [GitHub Actions],
20+
* Add ocumentation generated by [FORD],
21+
* Add runtime assertion-checking using [Assert], and
22+
* Add [JSON] file input/output using [jsonff].
2023

21-
DAG includes a topological sort feature, and it generates files in the [GraphViz] "dot" format.
24+
Constructor results contain an private index array that describes a topologically sorting
25+
of the vertices in a DAG.
2226

2327
Prerequisites
2428
-------------
25-
DAG was developed with the following prerequisite package versions:
29+
The [fpm] package manager automatically downloads and builds all dependencies.
30+
See [fpm.toml] for a complete list, including version numbers.
2631

27-
1. [gfortran] 10.2.0
28-
2. [OpenCoarrays] 2.9.2
29-
3. [fpm] 0.1.3
30-
4. [graphviz] 2.44.1
31-
32-
Earlier versions might work also.
33-
34-
Building and testing
32+
Downloading, building and testing
3533
--------------------
36-
After installing [fpm], clone, build, and test, execute the following in a `bash`-like shell:
34+
With [fpm] installed, clone
3735
```
3836
git clone [email protected]:sourceryinstitute/dag
39-
fpm test --compiler caf --runner "cafrun -n 1"
4037
```
41-
replacing `1` in the last line with the desired number of images to execute in parallel for
42-
each test. Also try compiling and running the example with
38+
### Serial build and testing
39+
Build and test the `dag` library with in a `bash`-like shell as follows:
40+
```
41+
fpm test
42+
```
43+
### Parallel building and testing with the gfortran/OpenCoarrays
44+
Build and test the `dag` library with in a `bash`-like shell as follows:
4345
```
44-
fpm run --example
46+
fpm test --compiler caf --runner "cafrun -n 2"
4547
```
48+
replacing `2` in the last line with the desired number of images to execute in parallel
49+
for each test.
50+
4651
Please report any test failures by submitting an [issue] on the DAG repository.
4752

48-
Example
49-
-------
53+
Examples
54+
--------
55+
56+
The [example] subdirectory provides the following examples:
5057

51-
The [./example/feats-dependency-tree.f90] program provides an example of the use of dag to
52-
describe the module dependencies in an early version of the Framework for Extensible Asynchronous
53-
Task Scheduling ([FEATS]). The example also writes the following image to a `.pdf` file:
58+
* [print-to-json.f90] - constructs and prints it a JSON representation the DAG.
59+
Run this example and redirect the output to a file as follows:
60+
```
61+
fpm run --example print-to-json > dag.json
62+
```
63+
* [read-from-json.f90] - constructs a DAG by reading from a JSON file.
64+
Run this example as follows:
65+
```
66+
fpm run --example read-from-json > dag.json
67+
```
68+
* [dag-to-dot.f90] - constructs a DAG describing the module dependencies in
69+
an early version of the Framework for Extensible Asynchronous
70+
Task Scheduling ([FEATS]) and prints the DAG to a .dot file suitable for
71+
conversion to a `.pdf` as follows:
72+
```
73+
fpm run --example dag-to-dot > feats-dag.dot
74+
dot -Tpdf -o feats.pdf feats.dot
75+
```
76+
which should produce the graph below.
5477

5578
![feats-dependencies](https://user-images.githubusercontent.com/13108868/133311851-721b7cda-1d10-4ee1-a51d-6169ca624839.png)
5679

@@ -75,6 +98,9 @@ This library is released under a [BSD-3 license].
7598
[JSON]: https://www.json.org/json-en.html
7699
[jsonff]: https://gitlab.com/everythingfunctional/jsonff
77100
[./src/dag_test.f90]: ./src/dag_test.f90
78-
[./example/feats-dependency-tree.f90]: ./example/feats-dependency-tree.f90
101+
[example]: ./example/
79102
[FEATS]: https://github.com/sourceryinstitute/feats
80103
[Assert]: https://github.com/sourceryinstitute/assert
104+
[print-to-json.f90]: ./example/print-to-json.f90
105+
[read-from-json.f90]:./example/read-from-json.f90
106+
[dag-to-dot.f90]: ./example/dag-to-dot.f90

example/dag-dependencies.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"vertices" : [
3+
{
4+
"label" : "iso_varying_string",
5+
"edges" : [
6+
]
7+
},
8+
{
9+
"label" : "assert_m ",
10+
"edges" : [
11+
]
12+
},
13+
{
14+
"label" : "vertex_s ",
15+
"edges" : [
16+
4,
17+
2
18+
]
19+
},
20+
{
21+
"label" : "vertex_m ",
22+
"edges" : [
23+
]
24+
},
25+
{
26+
"label" : "dag_m ",
27+
"edges" : [
28+
4
29+
]
30+
},
31+
{
32+
"label" : "dag_s ",
33+
"edges" : [
34+
5,
35+
2
36+
]
37+
},
38+
{
39+
"label" : "main ",
40+
"edges" : [
41+
5,
42+
4
43+
]
44+
}
45+
]
46+
}

example/dag-to-dot.f90

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
program dag_to_dot
2+
!! Demonstrate the construction of a complex DAG, the generation of a Graphviz
3+
!! .dot file suitable for conversion to Portable Document Format (PDF). The
4+
!! DAG in this example represents the module dependencies in an early version
5+
!! of the Framework for Exensible Asynchronous Task Scheduling (FEATS).
6+
!! See https://github.com/sourceryinstitute/feats.
7+
!!
8+
!! Compile and run this program by executing the following command
9+
!! with your present working directory set to the top-level directory
10+
!! in the DAG source tree:
11+
!!
12+
!! fpm run --example dag-to-dot > feats.dot
13+
!! dot -Tpdf -o feats.pdf feats.dot
14+
!!
15+
!! where the latter command produces a PDF file if Graphviz has been installed.
16+
!!
17+
use dag_m, only : dag_t
18+
use vertex_m, only : vertex_t
19+
use iso_varying_string, only : var_str, varying_string
20+
implicit none
21+
22+
character(len=*), parameter :: longest_name = "app_generator_m"
23+
character(len=len(longest_name)), parameter :: names(*) = &
24+
[character(len=len(longest_name)) :: &
25+
"assert_m", "dag_m", "payload_m", "compile_m", "data_loc_map_m", "task_m", "task_item_m", "app_m", "app_generator_m" &
26+
,"image_m", "main", "task_item_s", "compile_s", "app_generator_s", "data_loc_map_s", "payload_s", "app_s", "mailbox_m" &
27+
,"image_s", "final_task_m", "final_task_s" &
28+
]
29+
associate( &
30+
assert_m => findloc(names, "assert_m", dim=1) &
31+
,dag_m => findloc(names, "dag_m", dim=1) &
32+
,payload_m => findloc(names, "payload_m", dim=1) &
33+
,compile_m => findloc(names, "compile_m", dim=1) &
34+
,data_loc_map_m => findloc(names, "data_loc_map_m", dim=1) &
35+
,task_m => findloc(names, "task_m", dim=1) &
36+
,task_item_m => findloc(names, "task_item_m", dim=1) &
37+
,app_m => findloc(names, "app_m", dim=1) &
38+
,app_generator_m => findloc(names, "app_generator_m", dim=1) &
39+
,image_m => findloc(names, "image_m", dim=1) &
40+
,main => findloc(names, "main", dim=1) &
41+
,task_item_s => findloc(names, "task_item_s", dim=1) &
42+
,compile_s => findloc(names, "compile_s", dim=1) &
43+
,app_generator_s => findloc(names, "app_generator_s", dim=1) &
44+
,data_loc_map_s => findloc(names, "data_loc_map_s", dim=1) &
45+
,payload_s => findloc(names, "payload_s", dim=1) &
46+
,app_s => findloc(names, "app_s", dim=1) &
47+
,mailbox_m => findloc(names, "mailbox_m", dim=1) &
48+
,image_s => findloc(names, "image_s", dim=1) &
49+
,final_task_m => findloc(names, "final_task_m", dim=1) &
50+
,final_task_s => findloc(names, "final_task_s", dim=1) &
51+
)
52+
53+
block
54+
character(len=*), parameter :: external_ = 'shape=square,fillcolor="green",style=filled'
55+
character(len=*), parameter :: root = 'shape=circle,fillcolor="white",style=filled'
56+
character(len=*), parameter :: branch = 'shape=square,fillcolor="SlateGray1",style=filled'
57+
character(len=len(branch)), parameter :: leaf = 'shape=circle,fillcolor="cornsilk",style=filled'
58+
type(dag_t) feats
59+
type(varying_string) name_string(size(names))
60+
61+
name_string = var_str(names)
62+
63+
feats = &
64+
dag_t([ &
65+
vertex_t([integer::], name_string(assert_m), var_str(external_)) &
66+
,vertex_t([integer:: ], name_string(dag_m), var_str(external_)) &
67+
,vertex_t([integer::], name_string(payload_m), var_str(leaf) ) &
68+
,vertex_t([integer:: ], name_string(compile_m), var_str(leaf) ) &
69+
,vertex_t([integer::], name_string(data_loc_map_m), var_str(leaf) ) &
70+
,vertex_t([payload_m], name_string(task_m), var_str(branch) ) &
71+
,vertex_t([task_m], name_string(task_item_m), var_str(leaf) ) &
72+
,vertex_t([dag_m, task_item_m], name_string(app_m), var_str(branch) ) &
73+
,vertex_t([app_m, dag_m, task_item_m, compile_m], name_string(app_generator_m), var_str(branch) ) &
74+
,vertex_t([app_m, data_loc_map_m], name_string(image_m), var_str(branch) ) &
75+
,vertex_t([app_generator_m, image_m], name_string(main), var_str(root) ) &
76+
,vertex_t([task_item_m], name_string(task_item_s), var_str(root) ) &
77+
,vertex_t([compile_m], name_string(compile_s), var_str(branch) ) &
78+
,vertex_t([app_generator_m], name_string(app_generator_s), var_str(root) ) &
79+
,vertex_t([data_loc_map_m], name_string(data_loc_map_s), var_str(root) ) &
80+
,vertex_t([payload_m], name_string(payload_s), var_str(root) ) &
81+
,vertex_t([app_m, assert_m], name_string(app_s), var_str(root) ) &
82+
,vertex_t([payload_m], name_string(mailbox_m), var_str(branch) ) &
83+
,vertex_t([image_m, mailbox_m], name_string(image_s), var_str(root) ) &
84+
,vertex_t([data_loc_map_m, payload_m, task_m], name_string(final_task_m), var_str(branch) ) &
85+
,vertex_t([final_task_m], name_string(final_task_s), var_str(root) ) &
86+
])
87+
print *, feats%graphviz_digraph()
88+
end block
89+
end associate
90+
91+
end program

example/feats-dependency-tree.f90

Lines changed: 0 additions & 81 deletions
This file was deleted.

example/print-to-json.f90

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
program print_to_json
2+
!! Demonstrate writing a dag_t object in JSON format
3+
!!
4+
!! Compile and run run this program by executing the following
5+
!! command from the top-level directory of the dag source tree:
6+
!!
7+
!! fpm run --example print-to-json
8+
!!
9+
use dag_m, only: dag_t
10+
use vertex_m, only: vertex_t
11+
use iso_varying_string, only : varying_string, var_str, char
12+
implicit none
13+
14+
character(len=*), parameter :: longest_name = "iso_varying_string"
15+
character(len=len(longest_name)), parameter :: names(*) = &
16+
[character(len=len(longest_name)) :: "iso_varying_string", "assert_m", "vertex_s", "vertex_m", "dag_m", "dag_s", "main"]
17+
type(varying_string) name_string(size(names))
18+
19+
name_string = var_str(names)
20+
21+
associate( &
22+
iso_varying_string => findloc(names, "iso_varying_string", dim=1) &
23+
,assert_m => findloc(names, "assert_m", dim=1) &
24+
,vertex_s => findloc(names, "vertex_s", dim=1) &
25+
,vertex_m => findloc(names, "vertex_m", dim=1) &
26+
,dag_m => findloc(names, "dag_m", dim=1) &
27+
,dag_s => findloc(names, "dag_s", dim=1) &
28+
,main => findloc(names, "main", dim=1) &
29+
)
30+
block
31+
type(dag_t) program_units
32+
33+
program_units = dag_t([ &
34+
vertex_t( [integer::], name_string(iso_varying_string) ) &
35+
,vertex_t( [integer::], name_string(assert_m) ) &
36+
,vertex_t( [vertex_m, assert_m], name_string(vertex_s) ) &
37+
,vertex_t( [integer::], name_string(vertex_m) ) &
38+
,vertex_t( [vertex_m], name_string(dag_m) ) &
39+
,vertex_t( [dag_m, assert_m], name_string(dag_s) ) &
40+
,vertex_t( [dag_m, vertex_m], name_string(main) ) &
41+
])
42+
43+
associate(json_object => program_units%to_json())
44+
print *, char(json_object%to_expanded_string())
45+
end associate
46+
end block
47+
end associate
48+
end program

0 commit comments

Comments
 (0)