emit_standard_node_json Subroutine

subroutine emit_standard_node_json(network, layer_id, vertex_idx, nodes, num_nodes, max_nodes, inits, num_inits, max_inits)

Emit ONNX node records for a standard, non-GNN layer.

Arguments

Type IntentOptional Attributes Name
class(network_type), intent(in) :: network

Instance of the network

integer, intent(in) :: layer_id

Layer identifier and vertex position

integer, intent(in) :: vertex_idx

Layer identifier and vertex position

type(onnx_node_type), intent(inout) :: nodes(:)

Exported ONNX nodes

integer, intent(inout) :: num_nodes

Node counter and allocation limit

integer, intent(inout) :: max_nodes

Node counter and allocation limit

type(onnx_initialiser_type), intent(inout) :: inits(:)

Exported ONNX initialisers

integer, intent(inout) :: num_inits

Initialiser counter and allocation limit

integer, intent(inout) :: max_inits

Initialiser counter and allocation limit


Source Code

  subroutine emit_standard_node_json( &
       network, layer_id, vertex_idx, &
       nodes, num_nodes, max_nodes, &
       inits, num_inits, max_inits)
    !! Emit ONNX node records for a standard, non-GNN layer.
    use athena__onnx_utils, only: emit_initialisers, build_attributes_json, &
         emit_activation_node
    implicit none

    ! Arguments
    class(network_type), intent(in) :: network
    !! Instance of the network
    integer, intent(in) :: layer_id, vertex_idx
    !! Layer identifier and vertex position
    type(onnx_node_type), intent(inout) :: nodes(:)
    !! Exported ONNX nodes
    integer, intent(inout) :: num_nodes, max_nodes
    !! Node counter and allocation limit
    type(onnx_initialiser_type), intent(inout) :: inits(:)
    !! Exported ONNX initialisers
    integer, intent(inout) :: num_inits, max_inits
    !! Initialiser counter and allocation limit

    ! Local variables
    character(128) :: node_name, layer_name, input_name
    !! Temporary strings used to build node names
    character(:), allocatable :: suffix
    !! Optional activation suffix for an input tensor name
    integer :: j, input_layer_id, n_inputs
    !! Loop index, source layer identifier and input count
    character(128), allocatable :: input_list(:)
    !! Input tensor names
    character(4096) :: attr_json
    !! Pre-formatted JSON attributes

    write(node_name, '("node_", I0)') network%model(layer_id)%layer%id

    select case(trim(network%model(layer_id)%layer%type))
    case('full')
       layer_name = 'Gemm'
    case('conv')
       layer_name = 'Conv'
    case('pool')
       layer_name = to_camel_case( &
            trim(adjustl(network%model(layer_id)%layer%subtype)) // '_' // &
            trim(adjustl(network%model(layer_id)%layer%type)), &
            capitalise_first_letter = .true.)
    case('actv')
       layer_name = to_camel_case( &
            trim(adjustl(network%model(layer_id)%layer%subtype)), &
            capitalise_first_letter = .true.)
    case('flat')
       layer_name = 'Flatten'
    case('batc')
       layer_name = 'BatchNormalization'
    case('drop')
       layer_name = 'Dropout'
    case('nop')
       layer_name = to_camel_case( &
            trim(adjustl(network%model(layer_id)%layer%name)), &
            capitalise_first_letter = .true.)
    case default
       layer_name = 'Unknown'
    end select

    n_inputs = 0
    allocate(input_list(100))

    do j = 1, network%auto_graph%num_vertices
       input_layer_id = network%auto_graph%vertex(j)%id
       if(network%auto_graph%adjacency( &
            j, network%vertex_order(vertex_idx)) .eq. 0) cycle

       if(all(network%auto_graph%adjacency(:,j) .eq. 0))then
          write(input_name, '("input_",I0)') &
               network%model(input_layer_id)%layer%id
          suffix = ''
       else
          write(input_name, '("node_",I0)') &
               network%model(input_layer_id)%layer%id
          suffix = '_output'
          select type(prev => network%model(input_layer_id)%layer)
          class is(learnable_layer_type)
             if(prev%activation%name .ne. 'none')then
                suffix = '_' // trim(adjustl(prev%activation%name)) // &
                     '_output'
             end if
          end select
       end if

       n_inputs = n_inputs + 1
       write(input_list(n_inputs), '(A,A)') trim(adjustl(input_name)), suffix
    end do

    select type(layer => network%model(layer_id)%layer)
    class is(learnable_layer_type)
       do j = 1, size(layer%params)
          n_inputs = n_inputs + 1
          write(input_list(n_inputs), '(A,"_param",I0)') trim(node_name), j
       end do
    end select

    call build_attributes_json( &
         network%model(layer_id)%layer, trim(layer_name), attr_json)

    num_nodes = num_nodes + 1
    nodes(num_nodes)%name = trim(node_name)
    nodes(num_nodes)%op_type = trim(layer_name)
    allocate(nodes(num_nodes)%inputs(n_inputs))
    nodes(num_nodes)%inputs = input_list(1:n_inputs)
    allocate(nodes(num_nodes)%outputs(1))
    write(nodes(num_nodes)%outputs(1), '(A,"_output")') trim(node_name)
    nodes(num_nodes)%attributes_json = attr_json

    select type(layer => network%model(layer_id)%layer)
    class is(learnable_layer_type)
       call emit_initialisers(layer, trim(node_name), inits, num_inits, &
            max_inits)
       if(layer%activation%name .ne. 'none')then
          call emit_activation_node( &
               layer%activation%name, trim(node_name), '', &
               nodes, num_nodes, max_nodes)
       end if
    end select

    deallocate(input_list)

  end subroutine emit_standard_node_json