Skip to content

Commit 293786e

Browse files
author
Damian Rouson
authored
Merge pull request #51 from sourceryinstitute/functional-interface
Functional interface
2 parents 8368246 + 20f86ba commit 293786e

File tree

7 files changed

+168
-291
lines changed

7 files changed

+168
-291
lines changed

doc-generator.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
project: Directed Acyclic Graph Library
22
summary: A Fortran 2018 application programmer interface for representing directed acyclic graphs.
3-
src_dir: ../src
4-
src_dir: ../tests
3+
src_dir: src
54
output_dir: doc/html
65
preprocess: true
76
macro: FORD
@@ -17,7 +16,7 @@ sort: permission-alpha
1716
extra_mods: iso_fortran_env:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html
1817
iso_c_binding:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fC_005fBINDING.html#ISO_005fC_005fBINDING
1918
project_github: https://github.com/sourceryinstitute/dag
20-
author: Jacob Williams and Damian Rouson
19+
author: Jacob Williams, Damian Rouson, Robert Singleterry, and Brad Richardson
2120
print_creation_date: true
2221
creation_date: %Y-%m-%d %H:%M %z
2322
project_github: https://github.com/sourceryinstitute/dag

example/feats-dependency-tree.f90

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,27 @@ program labeled_dag_output
5050

5151
associate(feats => &
5252
dag_t([ &
53-
vertex_t(assert_m, [integer::], names(assert_m), var_str(external_)) &
54-
,vertex_t(dag_m, [integer:: ], names(dag_m), var_str(external_)) &
55-
,vertex_t(payload_m, [integer::], names(payload_m), var_str(leaf)) &
56-
,vertex_t(compile_m, [integer:: ], names(compile_m), var_str(leaf)) &
57-
,vertex_t(data_loc_map_m, [integer::], names(data_loc_map_m), var_str(leaf)) &
58-
,vertex_t(task_m, [payload_m], names(task_m), var_str(branch)) &
59-
,vertex_t(task_item_m, [task_m], names(task_item_m), var_str(leaf)) &
60-
,vertex_t(app_m, [dag_m, task_item_m], names(app_m), var_str(branch)) &
61-
,vertex_t(app_generator_m, [app_m, dag_m, task_item_m, compile_m], names(app_generator_m), var_str(branch)) &
62-
,vertex_t(image_m, [app_m, data_loc_map_m], names(image_m), var_str(branch)) &
63-
,vertex_t(main, [app_generator_m, image_m], names(main), var_str(root)) &
64-
,vertex_t(task_item_s, [task_item_m], names(task_item_s), var_str(root)) &
65-
,vertex_t(compile_s, [compile_m], names(compile_s), var_str(branch)) &
66-
,vertex_t(app_generator_s, [app_generator_m], names(app_generator_s), var_str(root)) &
67-
,vertex_t(data_loc_map_s, [data_loc_map_m], names(data_loc_map_s), var_str(root)) &
68-
,vertex_t(payload_s, [payload_m], names(payload_s), var_str(root)) &
69-
,vertex_t(app_s, [app_m, assert_m], names(app_s), var_str(root)) &
70-
,vertex_t(mailbox_m, [payload_m], names(mailbox_m), var_str(branch)) &
71-
,vertex_t(image_s, [image_m, mailbox_m], names(image_s), var_str(root)) &
72-
,vertex_t(final_task_m, [data_loc_map_m, payload_m, task_m], names(final_task_m), var_str(branch)) &
73-
,vertex_t(final_task_s, [final_task_m], names(final_task_s), var_str(root)) &
53+
vertex_t([integer::], names(assert_m), var_str(external_)) &
54+
,vertex_t([integer:: ], names(dag_m), var_str(external_)) &
55+
,vertex_t([integer::], names(payload_m), var_str(leaf)) &
56+
,vertex_t([integer:: ], names(compile_m), var_str(leaf)) &
57+
,vertex_t([integer::], names(data_loc_map_m), var_str(leaf)) &
58+
,vertex_t([payload_m], names(task_m), var_str(branch)) &
59+
,vertex_t([task_m], names(task_item_m), var_str(leaf)) &
60+
,vertex_t([dag_m, task_item_m], names(app_m), var_str(branch)) &
61+
,vertex_t([app_m, dag_m, task_item_m, compile_m], names(app_generator_m), var_str(branch)) &
62+
,vertex_t([app_m, data_loc_map_m], names(image_m), var_str(branch)) &
63+
,vertex_t([app_generator_m, image_m], names(main), var_str(root)) &
64+
,vertex_t([task_item_m], names(task_item_s), var_str(root)) &
65+
,vertex_t([compile_m], names(compile_s), var_str(branch)) &
66+
,vertex_t([app_generator_m], names(app_generator_s), var_str(root)) &
67+
,vertex_t([data_loc_map_m], names(data_loc_map_s), var_str(root)) &
68+
,vertex_t([payload_m], names(payload_s), var_str(root)) &
69+
,vertex_t([app_m, assert_m], names(app_s), var_str(root)) &
70+
,vertex_t([payload_m], names(mailbox_m), var_str(branch)) &
71+
,vertex_t([image_m, mailbox_m], names(image_s), var_str(root)) &
72+
,vertex_t([data_loc_map_m, payload_m, task_m], names(final_task_m), var_str(branch)) &
73+
,vertex_t([final_task_m], names(final_task_s), var_str(root)) &
7474
]))
7575
call feats%save_digraph(digraph_file, 'RL', 300)
7676
call execute_command_line('dot -Tpdf -o ' // output_file // ' ' // digraph_file)

src/dag_m.f90

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,34 @@ module dag_m
1515
type,public :: dag_t
1616
!! Encapsulate a graph as an array of vertices, each storing dependency information
1717
private
18-
type(vertex_t),dimension(:),allocatable :: vertices
18+
type(vertex_t), allocatable :: vertices(:)
1919
integer, allocatable :: order(:)
2020
contains
2121
procedure :: is_sorted_and_acyclic
2222
procedure :: to_json
23-
procedure :: save_digraph
2423
procedure :: num_vertices
2524
procedure :: dependencies_for
2625
procedure :: depends_on
26+
27+
procedure :: save_digraph
2728
generic :: write(formatted) => write_formatted
2829
generic :: read(formatted) => read_formatted
2930

3031
procedure, private :: write_formatted
3132
procedure, private :: read_formatted
32-
3333
end type
3434

3535
interface dag_t
3636

3737
module function construct_from_json(json_object) result(dag)
38+
!! Construct a dag_t object from a JSON object (result contains a topologically sorted index array)
3839
implicit none
3940
type(json_object_t), intent(in) :: json_object
4041
type(dag_t) dag
4142
end function
4243

43-
module function construct_from_components(vertices) result(dag)
44-
!! Construct an object containing the topologically sorted vertex ordering for a DAG
44+
pure module function construct_from_components(vertices) result(dag)
45+
!! Construct a dag_t object from an array of (unsorted) vertex_t objects (result contains a topologically sorted index array)
4546
implicit none
4647
type(vertex_t), intent(in) :: vertices(:)
4748
type(dag_t) dag
@@ -51,14 +52,15 @@ module function construct_from_components(vertices) result(dag)
5152

5253
interface
5354

54-
module function is_sorted_and_acyclic(self)
55+
pure module function is_sorted_and_acyclic(self)
5556
!! Result is true if dag%order contains a topological sorting of vertex identifiers
5657
implicit none
5758
class(dag_t), intent(in) :: self
5859
logical is_sorted_and_acyclic
5960
end function
6061

6162
module function to_json(self) result(json_object)
63+
!! Result is a JSON representation of the dag_t object
6264
implicit none
6365
class(dag_t), intent(in) :: self
6466
type(json_object_t) json_object

src/dag_s.f90

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,21 @@
1414

1515
implicit none
1616

17-
type vertex_status_t
18-
logical :: marked=.false., checking=.false.
19-
end type
20-
2117
contains
2218

2319
module procedure construct_from_components
2420
dag%vertices = vertices
25-
dag%order = toposort(dag)
21+
dag%order = topological_sort(dag)
2622
call assert(dag%is_sorted_and_acyclic(), "construct_from_components: dag%is_sorted_and_acyclic()")
2723
end procedure
2824

29-
module function toposort(dag) result(order)
25+
pure module function topological_sort(dag) result(order)
3026
!! Provide array of vertex numbers ordered in a way that respects dependencies
3127
type(dag_t), intent(in) :: dag
3228
integer, allocatable :: order(:), searched(:)
3329
integer v
3430

35-
call assert(all([(allocated(dag%vertices(v)%edges), v=1, size(dag%vertices))]), &
36-
"dag_s toposort: (all([(allocated(dag%vertices(v)%edges), v=1, size(dag%vertices))])")
31+
call assert(all(dag%vertices(:)%edges_allocated()), "dag_s topological_sort: all(dag%vertices(:)%edges_allocated())")
3732

3833
block
3934
integer, allocatable :: discovered(:)
@@ -51,7 +46,7 @@ module function toposort(dag) result(order)
5146

5247
contains
5348

54-
recursive subroutine depth_first_search(v, d, s, o)
49+
pure recursive subroutine depth_first_search(v, d, s, o)
5550
integer, intent(in) :: v, d(:)
5651
integer, intent(out), allocatable :: s(:)
5752
integer, intent(inout), allocatable :: o(:)
@@ -80,7 +75,7 @@ recursive subroutine depth_first_search(v, d, s, o)
8075

8176
end subroutine
8277

83-
end function toposort
78+
end function topological_sort
8479

8580
module procedure is_sorted_and_acyclic
8681

@@ -97,7 +92,7 @@ end function toposort
9792
integer i, j
9893

9994
do i = 1, num_vertices
100-
associate(edges => self%vertices(self%order(i))%get_edges())
95+
associate(edges => self%vertices(self%order(i))%edges())
10196
do j = 1, size(edges)
10297
if (.not. any(edges(j) == self%order(1:i))) then
10398
is_sorted_and_acyclic = .false.
@@ -116,11 +111,11 @@ end function toposort
116111

117112
module procedure construct_from_json
118113
type(fallible_json_value_t) :: maybe_vertices
114+
type(error_list_t) errors
119115

120116
maybe_vertices = json_object%get_element("vertices")
121-
associate(errors => maybe_vertices%errors())
122-
call assert(.not. errors%has_any(), "dag_s construct_from_json: .not. errors%has_any()", char(errors%to_string()))
123-
end associate
117+
errors = maybe_vertices%errors()
118+
call assert(.not. errors%has_any(), "dag_s construct_from_json: .not. errors%has_any()", char(errors%to_string()))
124119

125120
select type (vertices => maybe_vertices%value_())
126121
type is (json_array_t)
@@ -129,34 +124,32 @@ end function toposort
129124
class default
130125
call assert(.false., "dag%from_json: vertices was not an array", char(vertices%to_compact_string()))
131126
end select
132-
dag%order = toposort(dag)
127+
dag%order = topological_sort(dag)
133128
end procedure
134129

135130
module procedure to_json
136131
type(fallible_json_string_t) maybe_key
137132
type(error_list_t) errors
133+
type(json_string_t) vertices_key
134+
type(json_array_t) vertices_value
138135

139136
maybe_key = fallible_json_string_t("vertices")
140137
errors = maybe_key%errors()
141138
call assert(.not. errors%has_any(), "dag%to_json: .not. errors%has_any()", char(errors%to_string()))
142139

143-
block
144-
type(json_array_t) vertices_value
145140

146-
vertices_value = json_array_t(json_element_t(self%vertices%to_json()))
141+
vertices_value = json_array_t(json_element_t(self%vertices%to_json()))
147142

148-
associate(vertices_key => maybe_key%string())
149-
json_object = json_object_t([vertices_key], [json_element_t(vertices_value)])
150-
end associate
151-
end block
143+
vertices_key = maybe_key%string()
144+
json_object = json_object_t([vertices_key], [json_element_t(vertices_value)])
152145
end procedure
153146

154147
module procedure num_vertices
155148
num_vertices = size(self%vertices)
156149
end procedure
157150

158151
module procedure dependencies_for
159-
dependency_ids = self%vertices(vertex_id)%get_edges()
152+
dependency_ids = self%vertices(vertex_id)%edges()
160153
end procedure
161154

162155
module procedure depends_on
@@ -169,7 +162,7 @@ end function toposort
169162
integer v
170163

171164
do v = 1, size(self%vertices)
172-
if (any(self%vertices(v)%edges == vertex_num)) dependencies = [dependencies, v]
165+
if (any(self%vertices(v)%edges() == vertex_num)) dependencies = [dependencies, v]
173166
end do
174167
end block
175168

@@ -193,6 +186,20 @@ end function toposort
193186
close(iunit,iostat=istat)
194187
contains
195188

189+
elemental function integer_to_string(i) result(s)
190+
integer,intent(in) :: i
191+
integer, parameter :: max_number_width = 64
192+
character(len=max_number_width) :: s
193+
integer :: istat
194+
195+
write(s,fmt='(ss,I0)',iostat=istat) i
196+
if (istat==0) then
197+
s = trim(adjustl(s))
198+
else
199+
s = '***'
200+
end if
201+
end function integer_to_string
202+
196203
function generate_digraph(self,rankdir,dpi) result(str)
197204
!! - Result is the string to write out to a *.dot file. (Called by save_digraph())
198205
implicit none
@@ -210,6 +217,9 @@ function generate_digraph(self,rankdir,dpi) result(str)
210217
character(len=*),parameter :: tab = ' '
211218
character(len=*),parameter :: newline = new_line(' ')
212219

220+
221+
call assert(all(self%vertices(:)%edges_allocated()), "generate_digraph: self%edges_allocated()")
222+
213223
str = 'digraph G {'//newline//newline
214224
if (present(rankdir)) &
215225
str = str//tab//'rankdir='//rankdir//newline//newline
@@ -218,24 +228,24 @@ function generate_digraph(self,rankdir,dpi) result(str)
218228

219229
! define the vertices:
220230
do i=1,size(self%vertices)
221-
label = 'label="'//trim(adjustl(self%vertices(i)%get_label()))//'"'
222-
attributes = '['//trim(adjustl(self%vertices(i)%get_attributes()))//','//label//']'
231+
label = 'label="'//trim(adjustl(self%vertices(i)%label()))//'"'
232+
attributes = '['//trim(adjustl(self%vertices(i)%attributes()))//','//label//']'
223233
str = str//tab//integer_to_string(i)//' '//attributes//newline
224234
if (i==size(self%vertices)) str = str//newline
225235
end do
226236

227237
! define the dependencies:
228238
do i=1,size(self%vertices)
229-
if (allocated(self%vertices(i)%edges)) then
230-
n_edges = size(self%vertices(i)%edges)
231-
str = str//tab//integer_to_string(i)//merge(' -> ',' ',n_edges/=0)
232-
do j=1,n_edges
233-
! comma-separated list:
234-
str = str//integer_to_string(self%vertices(i)%edges(j))
235-
if (n_edges>1 .and. j<n_edges) str = str//','
236-
end do
237-
str = str//';'//newline
238-
end if
239+
n_edges = size(self%vertices(i)%edges())
240+
str = str//tab//integer_to_string(i)//merge(' -> ',' ',n_edges/=0)
241+
do j=1,n_edges
242+
! comma-separated list:
243+
associate(edges => self%vertices(i)%edges())
244+
str = str//integer_to_string(edges(j))
245+
if (n_edges>1 .and. j<n_edges) str = str//','
246+
end associate
247+
end do
248+
str = str//';'//newline
239249
end do
240250

241251
str = str//newline//'}'
@@ -244,23 +254,6 @@ end function generate_digraph
244254

245255
end procedure
246256

247-
elemental function integer_to_string(i) result(s)
248-
249-
integer,intent(in) :: i
250-
integer, parameter :: max_number_width = 64
251-
character(len=max_number_width) :: s
252-
253-
integer :: istat
254-
255-
write(s,fmt='(ss,I0)',iostat=istat) i
256-
if (istat==0) then
257-
s = trim(adjustl(s))
258-
else
259-
s = '***'
260-
end if
261-
262-
end function integer_to_string
263-
264257
module procedure read_formatted
265258

266259
character(len=*), parameter :: NEWLINE = NEW_LINE('A')

0 commit comments

Comments
 (0)