Skip to content

Commit

Permalink
adding ability to add metadata to nodes and edges
Browse files Browse the repository at this point in the history
some code cleanup
added get_edge_index method
  • Loading branch information
jacobwilliams committed Dec 31, 2023
1 parent 0bcac21 commit 966eef8
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 54 deletions.
182 changes: 130 additions & 52 deletions src/dag_module.F90
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module dag_module
integer(ip) :: ivertex = 0 !! vertex number (the index in the [[dag]] `vertices` array)
character(len=:),allocatable :: label !! used for diagraph
character(len=:),allocatable :: attributes !! used for diagraph
class(*),allocatable :: metadata !! user-defined metadata
end type edge
interface edge
!! constructor for an [[edge]] type.
Expand All @@ -48,6 +49,7 @@ module dag_module
logical :: marked = .false. !! used for toposort
character(len=:),allocatable :: label !! used for diagraph
character(len=:),allocatable :: attributes !! used for diagraph
class(*),allocatable :: metadata !! user-defined metadata
contains
private
generic :: set_edges => set_edge_vector_vector, add_edge
Expand All @@ -67,21 +69,25 @@ module dag_module

procedure,public :: vertex => dag_get_vertex !! not very useful for now, since
!! all vertex attributes are private
procedure,public :: number_of_vertices => dag_get_number_of_vertices
procedure,public :: set_vertices => dag_set_vertices
generic,public :: set_edges => dag_set_edges_no_atts, &
dag_set_edges_vector_atts
procedure,public :: add_edge => dag_add_edge
procedure,public :: remove_edge => dag_remove_edge
procedure,public :: remove_vertex => dag_remove_node
procedure,public :: set_vertex_info => dag_set_vertex_info
procedure,public :: toposort => dag_toposort
procedure,public :: generate_digraph => dag_generate_digraph
procedure,public :: number_of_vertices => dag_get_number_of_vertices
procedure,public :: get_edge_metadata => dag_get_edge_metadata
procedure,public :: get_vertex_metadata => dag_get_vertex_metadata
procedure,public :: get_edges => dag_get_edges
procedure,public :: get_dependencies => dag_get_dependencies

procedure,public :: set_vertices => dag_set_vertices
procedure,public :: set_vertex_info => dag_set_vertex_info
procedure,public :: add_edge => dag_add_edge
generic,public :: set_edges => dag_set_edges_no_atts, &
dag_set_edges_vector_atts
procedure,public :: remove_edge => dag_remove_edge
procedure,public :: remove_vertex => dag_remove_node
procedure,public :: toposort => dag_toposort
procedure,public :: generate_digraph => dag_generate_digraph
procedure,public :: generate_dependency_matrix => dag_generate_dependency_matrix
procedure,public :: save_digraph => dag_save_digraph
procedure,public :: get_edges => dag_get_edges
procedure,public :: get_dependencies => dag_get_dependencies
procedure,public :: destroy => dag_destroy
procedure,public :: save_digraph => dag_save_digraph
procedure,public :: destroy => dag_destroy
procedure,public :: get_edge_index

procedure :: init_internal_vars !! private routine to initialize some internal variables
procedure :: dag_set_edges_no_atts, dag_set_edges_vector_atts
Expand All @@ -95,15 +101,18 @@ module dag_module
!>
! Constructor for [[edge]] type.

pure elemental function edge_constructor(ivertex,label,attributes) result(e)
pure elemental function edge_constructor(ivertex,label,attributes,metadata) result(e)

integer(ip),intent(in),optional :: ivertex !! vertex number defining the destination of this edge
character(len=*),intent(in),optional :: label !! vertex name for grahviz
character(len=*),intent(in),optional :: attributes !! other attributes for graphviz
class(*),intent(in),optional :: metadata !! optional user-defined metadata

integer(ip),intent(in),optional :: ivertex
character(len=*),intent(in),optional :: label
character(len=*),intent(in),optional :: attributes
type(edge) :: e
e%ivertex = ivertex
if (present(label)) e%label = label
if (present(attributes)) e%attributes = attributes
if (present(metadata)) allocate(e%attributes, source = attributes)

end function edge_constructor
!*******************************************************************************
Expand All @@ -126,27 +135,19 @@ end subroutine dag_destroy
!>
! specify the edge indices for this vertex

subroutine set_edge_vector_vector(me,edges,label,attributes)
subroutine set_edge_vector_vector(me,edges,label,attributes,metadata)

class(vertex),intent(inout) :: me
integer(ip),dimension(:),intent(in) :: edges
character(len=*),dimension(:),intent(in),optional :: label
character(len=*),dimension(:),intent(in),optional :: attributes !! other attributes when
!! saving as a diagraph.
class(*),intent(in),optional :: metadata !! optional user-defined metadata

integer(ip) :: i !! counter

do i=1,size(edges)
if (present(label) .and. present(attributes)) then
call me%add_edge(edges(i),label=label(i),attributes=attributes(i))
else if (.not. present(label) .and. present(attributes)) then
call me%add_edge(edges(i),attributes=attributes(i))
else if (present(label) .and. .not. present(attributes)) then
call me%add_edge(edges(i),label=label(i))
else
call me%add_edge(edges(i))
end if
end do
! elemental assignment:
me%edges = edge(ivertex=edges,label=label,&
attributes=attributes,metadata=metadata)
call sort_ascending(me%edges)

end subroutine set_edge_vector_vector
!*******************************************************************************
Expand All @@ -155,21 +156,27 @@ end subroutine set_edge_vector_vector
!>
! add an edge index for this vertex

subroutine add_edge(me,e,label,attributes)
subroutine add_edge(me,e,label,attributes,metadata)

class(vertex),intent(inout) :: me
integer(ip),intent(in) :: e
character(len=*),intent(in),optional :: label
character(len=*),intent(in),optional :: attributes !! other attributes when
!! saving as a diagraph.
class(*),intent(in),optional :: metadata !! optional user-defined metadata

type(edge) :: edge_

edge_ = edge(ivertex=e,label=label,&
attributes=attributes,metadata=metadata)

if (allocated(me%edges)) then
if (.not. any(e==me%edges%ivertex)) then ! don't add if already there
me%edges = [me%edges, edge(e,label=label,attributes=attributes)]
me%edges = [me%edges, edge_]
call sort_ascending(me%edges)
end if
else
me%edges = [edge(e,label=label,attributes=attributes)]
me%edges = [edge_]
end if

end subroutine add_edge
Expand Down Expand Up @@ -261,8 +268,10 @@ pure function dag_get_edges(me,ivertex) result(edges)
integer(ip),intent(in) :: ivertex
integer(ip),dimension(:),allocatable :: edges

if (ivertex>0 .and. ivertex <= me%n) then
edges = me%vertices(ivertex)%edges%ivertex ! auto LHS allocation
if (allocated(me%vertices(ivertex)%edges)) then
if (ivertex>0 .and. ivertex <= me%n) then
edges = me%vertices(ivertex)%edges%ivertex ! auto LHS allocation
end if
end if

end function dag_get_edges
Expand Down Expand Up @@ -310,31 +319,38 @@ end function dag_get_dependencies
! * [[dag_set_vertex_info]] which can be used to set/change
! the labels and other attributes.

subroutine dag_set_vertices(me,nvertices,labels)
subroutine dag_set_vertices(me,nvertices,labels,attributes,metadata)

class(dag),intent(inout) :: me
integer(ip),intent(in) :: nvertices !! number of vertices
character(len=*),dimension(nvertices),intent(in),optional :: labels !! vertex name strings
character(len=*),intent(in),optional :: attributes !! other attributes when
!! saving as a diagraph.
class(*),intent(in),optional :: metadata !! optional user-defined metadata

integer(ip) :: i !! counter
logical :: has_label !! if `labels` is specified
character(len=:),allocatable :: label_ !! temp variable for labels

if (nvertices<=0) error stop 'error: nvertices must be >= 1'

if (allocated(me%vertices)) deallocate(me%vertices)

me%n = nvertices
allocate(me%vertices(nvertices))
me%vertices%ivertex = [(i,i=1,nvertices)]
me%vertices%ivertex = [(i,i=1,nvertices)] ! vertex indices

if (present(labels)) then
do i = 1, nvertices
me%vertices(i)%label = trim(adjustl(labels(i)))
end do
else
! just use the vertex number
do i = 1, nvertices
me%vertices(i)%label = integer_to_string(i)
end do
end if
has_label = present(labels)

do i = 1, nvertices
if (has_label) then
label_ = trim(adjustl(labels(i)))
else
label_ = integer_to_string(i) ! just use the vertex number
end if
call me%set_vertex_info(ivertex=i,label=label_,&
attributes=attributes,metadata=metadata)
end do

end subroutine dag_set_vertices
!*******************************************************************************
Expand All @@ -353,11 +369,69 @@ pure function dag_get_number_of_vertices(me) result(nvertices)
end function dag_get_number_of_vertices
!*******************************************************************************

!*******************************************************************************
!>
! Returns the metadata for a vertex (node) in the dag.

pure function dag_get_vertex_metadata(me,ivertex) result(m)

class(dag),intent(in) :: me
integer(ip),intent(in) :: ivertex !! vertex number
class(*),allocatable :: m

if (allocated(me%vertices(ivertex)%metadata)) &
allocate(m, source = me%vertices(ivertex)%metadata)

end function dag_get_vertex_metadata
!*******************************************************************************

!*******************************************************************************
!>
! Returns the metadata for an edge in the dag.

pure function dag_get_edge_metadata(me,ivertex,iedge) result(m)

class(dag),intent(in) :: me
integer(ip),intent(in) :: ivertex !! vertex number
integer(ip),intent(in) :: iedge !! edge vertex
class(*),allocatable :: m

associate ( i => me%get_edge_index(ivertex,iedge) )
if (i>0) allocate(m, source = me%vertices(ivertex)%edges(i)%metadata)
end associate

end function dag_get_edge_metadata
!*******************************************************************************

!*******************************************************************************
!>
! Returns the index in the edge array of the vertex.

pure function get_edge_index(me,ivertex,iedge) result(edge_index)

class(dag),intent(in) :: me
integer(ip),intent(in) :: ivertex !! vertex number
integer(ip),intent(in) :: iedge !! edge vertex number
integer(ip) :: edge_index !! the index of the `iedge` vertex in
!! the edge array (0 if not found)

integer(ip),dimension(1) :: idx

if (allocated(me%vertices(ivertex)%edges)) then
idx = findloc(me%vertices(ivertex)%edges%ivertex, iedge)
edge_index = idx(1)
else
edge_index = 0_ip
end if

end function get_edge_index
!*******************************************************************************

!*******************************************************************************
!>
! set info about a vertex in a dag.

subroutine dag_set_vertex_info(me,ivertex,label,attributes)
subroutine dag_set_vertex_info(me,ivertex,label,attributes,metadata)

class(dag),intent(inout) :: me
integer(ip),intent(in) :: ivertex !! vertex number
Expand All @@ -366,9 +440,11 @@ subroutine dag_set_vertex_info(me,ivertex,label,attributes)
!! number is used.
character(len=*),intent(in),optional :: attributes !! other attributes when
!! saving as a diagraph.
class(*),intent(in),optional :: metadata !! optional user-defined metadata

if (present(label)) me%vertices(ivertex)%label = label
if (present(attributes)) me%vertices(ivertex)%attributes = attributes
if (present(metadata)) allocate(me%vertices(ivertex)%metadata, source=metadata)

end subroutine dag_set_vertex_info
!*******************************************************************************
Expand Down Expand Up @@ -398,18 +474,20 @@ end function dag_get_vertex
!>
! Add an edge to a dag.

subroutine dag_add_edge(me,ivertex,iedge,label,attributes)
subroutine dag_add_edge(me,ivertex,iedge,label,attributes,metadata)

class(dag),intent(inout) :: me
integer(ip),intent(in) :: ivertex !! vertex number
integer(ip),intent(in) :: iedge !! the vertex to connect to `ivertex`
character(len=*),intent(in),optional :: label !! edge label
character(len=*),intent(in),optional :: attributes !! other attributes when
!! saving as a diagraph.
class(*),intent(in),optional :: metadata !! optional user-defined metadata

call me%vertices(ivertex)%set_edges(iedge,&
label=label,&
attributes=attributes)
attributes=attributes,&
metadata=metadata)

end subroutine dag_add_edge
!*******************************************************************************
Expand Down
6 changes: 4 additions & 2 deletions test/dag_example_3.f90
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ program dag_example_3
implicit none

type(dag) :: d
integer(ip) :: i, n_nodes
integer(ip) :: i, j, n_nodes
character(len=3),dimension(:),allocatable :: labels

character(len=*),parameter :: filetype = 'pdf' !! filetype for output plot ('pdf', png', etc.)

n_nodes = 0
!allocate(labels(0))
do i = 1, 2
! first pass just gets the nodes, 2nd gets the dependencies
call process(i, 'jqt', ['rhn', 'xhk', 'nvd'])
Expand All @@ -36,6 +35,9 @@ program dag_example_3
if (i==1) then
write(*,*) 'set_vertices !'
call d%set_vertices(n_nodes, labels=labels)
do j = 1, n_nodes
call d%set_vertex_info(j,attributes='fillcolor=cornsilk,style=filled')
end do
end if
end do

Expand Down

0 comments on commit 966eef8

Please sign in to comment.