build_network_from_json_standard Subroutine

subroutine build_network_from_json_standard(network, nodes, num_nodes, inits, num_inits, inputs, num_inputs, verbose_)

Build a standard, non-GNN network from parsed JSON data.

Synthetic value_info entries are created for layers whose output shape can be inferred from initialisers or simple attributes before calling build_from_onnx.

Arguments

Type IntentOptional Attributes Name
type(network_type), intent(inout) :: network

Network to populate from parsed ONNX content

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

Parsed ONNX nodes

integer, intent(in) :: num_nodes

Number of valid entries in nodes

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

Parsed ONNX initialisers

integer, intent(in) :: num_inits

Number of valid entries in inits

type(onnx_tensor_type), intent(in) :: inputs(:)

Parsed graph input tensors

integer, intent(in) :: num_inputs

Number of valid entries in inputs

integer, intent(in) :: verbose_

Effective verbosity level


Source Code

  subroutine build_network_from_json_standard( &
       network, nodes, num_nodes, inits, num_inits, inputs, num_inputs, &
       verbose_)
    !! Build a standard, non-GNN network from parsed JSON data.
    !!
    !! Synthetic value_info entries are created for layers whose output shape
    !! can be inferred from initialisers or simple attributes before calling
    !! build_from_onnx.
    implicit none

    ! Arguments
    type(network_type), intent(inout) :: network
    !! Network to populate from parsed ONNX content
    type(onnx_node_type), intent(in) :: nodes(:)
    !! Parsed ONNX nodes
    integer, intent(in) :: num_nodes
    !! Number of valid entries in nodes
    type(onnx_initialiser_type), intent(in) :: inits(:)
    !! Parsed ONNX initialisers
    integer, intent(in) :: num_inits
    !! Number of valid entries in inits
    type(onnx_tensor_type), intent(in) :: inputs(:)
    !! Parsed graph input tensors
    integer, intent(in) :: num_inputs
    !! Number of valid entries in inputs
    integer, intent(in) :: verbose_
    !! Effective verbosity level

    ! Local variables
    type(onnx_tensor_type), allocatable :: value_infos(:)
    !! Synthesised tensor value_info entries
    integer :: i, j, k, num_vi, ndims, n_kernel_dims
    !! Loop indices and temporary dimension counters
    character(128) :: out_name
    !! Current node output tensor name
    character(32) :: op_type_name
    !! Current node ONNX op type

    if(is_onnx_expanded_nop_graph(nodes, num_nodes))then
       call build_network_from_json_onnx_expanded_nop( &
            network, nodes, num_nodes, inits, num_inits, verbose_)
       return
    end if

    if(is_onnx_expanded_gnn_graph(nodes, num_nodes))then
       call build_network_from_json_onnx_expanded_gnn( &
            network, nodes, num_nodes, &
            inits, num_inits, &
            inputs, num_inputs, verbose_)
       return
    end if

    allocate(value_infos(num_nodes))
    num_vi = 0

    node_loop: do i = 1, num_nodes
       if(.not.allocated(nodes(i)%outputs)) cycle
       if(nodes(i)%num_outputs .lt. 1) cycle

       out_name = trim(nodes(i)%outputs(1))
       op_type_name = trim(adjustl(nodes(i)%op_type))

       do j = 1, nodes(i)%num_inputs
          do k = 1, num_inits
             if(trim(nodes(i)%inputs(j)) .ne. trim(inits(k)%name)) cycle
             if(.not.allocated(inits(k)%dims)) cycle
             if(size(inits(k)%dims) .lt. 2) cycle

             num_vi = num_vi + 1
             value_infos(num_vi)%name = out_name
             value_infos(num_vi)%elem_type = 1
             ndims = size(inits(k)%dims)

             if(op_type_name .eq. 'Conv' .and. ndims .ge. 3)then
                allocate(value_infos(num_vi)%dims(ndims))
                value_infos(num_vi)%dims(1) = 1
                value_infos(num_vi)%dims(2) = inits(k)%dims(ndims)
                value_infos(num_vi)%dims(3:ndims) = 0
             else
                allocate(value_infos(num_vi)%dims(2))
                value_infos(num_vi)%dims(1) = 1
                value_infos(num_vi)%dims(2) = inits(k)%dims(1)
             end if
             cycle node_loop
          end do
       end do

       if(index(op_type_name, 'Pool', back=.true.) .eq. &
            len_trim(op_type_name) - 3)then
          n_kernel_dims = 0
          if(allocated(nodes(i)%attributes))then
             do j = 1, size(nodes(i)%attributes)
                if(trim(adjustl(nodes(i)%attributes(j)%name)) .ne. &
                     'kernel_shape') cycle
                block
                  character(256) :: kval
                  integer :: kpos, kstat, ktemp

                  kval = trim(adjustl(nodes(i)%attributes(j)%val))
                  kpos = 1
                  do while(kpos .le. len_trim(kval))
                     do while(kpos .le. len_trim(kval) .and. &
                          kval(kpos:kpos) .eq. ' ')
                        kpos = kpos + 1
                     end do
                     if(kpos .gt. len_trim(kval)) exit
                     read(kval(kpos:), *, iostat=kstat) ktemp
                     if(kstat .ne. 0) exit
                     n_kernel_dims = n_kernel_dims + 1
                     do while(kpos .le. len_trim(kval) .and. &
                          kval(kpos:kpos) .ne. ' ')
                        kpos = kpos + 1
                     end do
                  end do
                end block
                exit
             end do
          end if

          if(n_kernel_dims .gt. 0)then
             num_vi = num_vi + 1
             value_infos(num_vi)%name = out_name
             value_infos(num_vi)%elem_type = 1
             allocate(value_infos(num_vi)%dims(n_kernel_dims + 2))
             value_infos(num_vi)%dims = 0
          end if
       end if

    end do node_loop

    call network%build_from_onnx( &
         nodes(1:num_nodes), inits(1:num_inits), inputs(1:num_inputs), &
         value_infos(1:num_vi), verbose=verbose_)

  end subroutine build_network_from_json_standard