GCC Code Coverage Report


Directory: src/athena/
File: src/athena/athena_onnx_read_sub.f90
Date: 2026-04-15 16:08:59
Exec Total Coverage
Lines: 643 871 73.8%
Functions: 0 0 -%
Branches: 1294 3683 35.1%

Line Branch Exec Source
1 submodule(athena__onnx) athena__onnx_read_submodule
2 !! Submodule containing the ONNX import procedures.
3 !!
4 !! This submodule contains the routines that parse the JSON ONNX
5 !! representation and rebuild ATHENA networks from it.
6 use athena__misc_types, only: onnx_attribute_type, onnx_node_type, &
7 onnx_initialiser_type, onnx_tensor_type
8 use coreutils, only: real32, to_lower, stop_program
9 implicit none
10
11 integer, parameter :: MAX_ITEMS = 500
12 integer, parameter :: MAX_GNN_METADATA = 100
13
14 type :: json_parse_result_type
15 type(onnx_node_type) :: nodes(MAX_ITEMS)
16 !! Parsed ONNX nodes
17 integer :: num_nodes = 0
18 !! Number of valid entries in nodes
19 type(onnx_initialiser_type) :: inits(MAX_ITEMS)
20 !! Parsed ONNX initialisers
21 integer :: num_inits = 0
22 !! Number of valid entries in inits
23 type(onnx_tensor_type) :: inputs(MAX_ITEMS)
24 !! Parsed graph input tensors
25 integer :: num_inputs = 0
26 !! Number of valid entries in inputs
27 type(onnx_tensor_type) :: outputs(MAX_ITEMS)
28 !! Parsed graph output tensors
29 integer :: num_outputs = 0
30 !! Number of valid entries in outputs
31 character(256) :: meta_keys(MAX_GNN_METADATA)
32 !! Metadata keys read from metadataProps
33 character(256) :: meta_values(MAX_GNN_METADATA)
34 !! Metadata values read from metadataProps
35 integer :: num_meta = 0
36 !! Number of valid metadata key/value pairs
37 end type json_parse_result_type
38
39 type :: json_node_state_type
40 logical :: in_object = .false.
41 !! Whether parser is currently inside a node object
42 logical :: in_attribute = .false.
43 !! Whether parser is currently inside an attribute array
44 integer :: attribute_bracket_depth = 0
45 !! Current square-bracket nesting depth inside a multiline attribute block
46 character(256) :: name = ''
47 !! Node name parsed from JSON
48 character(256) :: op_type = ''
49 !! Node opType parsed from JSON
50 character(128), allocatable :: inputs(:)
51 !! Temporary node input names
52 character(128), allocatable :: outputs(:)
53 !! Temporary node output names
54 integer :: num_inputs = 0
55 !! Number of valid input names
56 integer :: num_outputs = 0
57 !! Number of valid output names
58 character(16) :: active_string_array = ''
59 !! Currently active multiline string array: input or output
60 type(onnx_attribute_type), allocatable :: attrs(:)
61 !! Temporary parsed node attributes
62 integer :: num_attrs = 0
63 !! Number of valid attribute entries
64 end type json_node_state_type
65
66 type :: json_initialiser_state_type
67 logical :: in_object = .false.
68 !! Whether parser is currently inside an initialiser object
69 logical :: in_dims_array = .false.
70 !! Whether parser is currently inside a multiline dims array
71 character(128) :: name = ''
72 !! Initialiser tensor name
73 integer :: data_type = 1
74 !! ONNX dataType enum value
75 integer, allocatable :: dims(:)
76 !! Parsed tensor dimensions
77 character(:), allocatable :: raw_data
78 !! Base64 payload from rawData field
79 end type json_initialiser_state_type
80
81 type :: json_tensor_state_type
82 logical :: in_object = .false.
83 !! Whether parser is currently inside a tensor object
84 integer :: object_depth = 0
85 !! Nested JSON object depth within this tensor block
86 character(128) :: name = ''
87 !! Tensor name
88 integer :: elem_type = 1
89 !! ONNX element type enum value
90 integer, allocatable :: dim_values(:)
91 !! Parsed tensor dimensions (-1 for dimParam)
92 end type json_tensor_state_type
93
94 type :: json_parser_state_type
95 character(32) :: section = ''
96 !! Active top-level section name
97 type(json_node_state_type) :: node
98 !! Reusable node parser state
99 type(json_initialiser_state_type) :: initialiser
100 !! Reusable initialiser parser state
101 type(json_tensor_state_type) :: input_tensor
102 !! Reusable input tensor parser state
103 type(json_tensor_state_type) :: output_tensor
104 !! Reusable output tensor parser state
105 end type json_parser_state_type
106
107 contains
108
109 !###############################################################################
110 12 module function read_onnx(file, verbose) result(network)
111 !! Import a network from ONNX JSON format.
112 !!
113 !! The parser keeps section-specific state in small helper types so the
114 !! main procedure only coordinates file I/O and dispatch.
115 implicit none
116
117 ! Arguments
118 character(*), intent(in) :: file
119 !! File to import the network from
120 integer, intent(in), optional :: verbose
121 !! Verbosity level
122
123 type(network_type) :: network
124 !! Resulting network instance
125
126 ! Local variables
127 integer :: unit, stat, verbose_
128 !! File unit, I/O status and effective verbosity
129 character(131072) :: line
130 !! File input buffer sized for large base64-encoded initialisers
131 12 character(:), allocatable :: trimmed
132 !! Current input line with leading and trailing whitespace removed
133
22/44
✓ Branch 0 taken 6000 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6000 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6000 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6000 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6000 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 6000 times.
✓ Branch 19 taken 12 times.
✓ Branch 20 taken 6000 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 6000 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 6000 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 6000 times.
✓ Branch 28 taken 6000 times.
✓ Branch 29 taken 12 times.
✓ Branch 30 taken 6000 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 33 taken 6000 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 6000 times.
✓ Branch 36 taken 6000 times.
✓ Branch 37 taken 12 times.
✓ Branch 38 taken 6000 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✓ Branch 41 taken 6000 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 6000 times.
24012 type(json_parse_result_type) :: parsed
134 !! Parsed ONNX records collected across all sections
135 12 type(json_parser_state_type) :: parser
136 !! Section-specific parser state
137 logical :: has_gnn
138 !! Whether the parsed model contains ATHENA GNN metadata
139
140
141 !--------------------------------------------------------------------------
142 ! Initialise options and parser state
143 !--------------------------------------------------------------------------
144
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if(present(verbose))then
145 12 verbose_ = verbose
146 else
147 verbose_ = 0
148 end if
149
150 12 call initialise_json_parser(parser)
151
152
153 !--------------------------------------------------------------------------
154 ! Read and dispatch JSON lines
155 !--------------------------------------------------------------------------
156 12 open(newunit=unit, file=file, status='old', action='read', iostat=stat)
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(stat .ne. 0)then
158 write(*,*) 'ERROR: Could not open file: ', trim(file)
159 return
160 end if
161
162 3945 do
163 3957 read(unit, '(A)', iostat=stat) line
164
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3945 times.
3957 if(stat .ne. 0) exit
165
166
9/10
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 3933 times.
✓ Branch 4 taken 3599 times.
✓ Branch 5 taken 334 times.
✓ Branch 6 taken 3939 times.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3939 times.
✓ Branch 10 taken 3939 times.
✓ Branch 11 taken 6 times.
3945 trimmed = trim(adjustl(line))
167
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3939 times.
3945 if(len_trim(trimmed) .eq. 0) cycle
168
169 3939 call detect_json_section(trimmed, parser)
170
171 7678 select case(trim(parser%section))
172 case('node')
173 call parse_node_section_line(trimmed, parser%node, parsed, &
174 2749 parser%section)
175 case('initializer')
176 call parse_initialiser_section_line(trimmed, parser%initialiser, &
177 306 parsed, parser%section)
178 case('input')
179 call parse_tensor_section_line(trimmed, parser%input_tensor, &
180 414 parsed%inputs, parsed%num_inputs, parser%section)
181 case('output')
182 call parse_tensor_section_line(trimmed, parser%output_tensor, &
183 246 parsed%outputs, parsed%num_outputs, parser%section)
184 case('metadata')
185
8/8
✓ Branch 0 taken 3739 times.
✓ Branch 1 taken 200 times.
✓ Branch 2 taken 2749 times.
✓ Branch 3 taken 306 times.
✓ Branch 4 taken 414 times.
✓ Branch 5 taken 246 times.
✓ Branch 6 taken 24 times.
✓ Branch 7 taken 200 times.
7678 call parse_metadata_line(trimmed, parsed, parser%section)
186 end select
187 end do
188
189 12 close(unit)
190
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(verbose_ .gt. 0)then
192 write(*,*) 'JSON parse: ', parsed%num_nodes, ' nodes, ', &
193 parsed%num_inits, ' initialisers, ', parsed%num_inputs, &
194 ' inputs, ', parsed%num_outputs, ' outputs, ', &
195 parsed%num_meta, ' metadata'
196 end if
197
198
199 !--------------------------------------------------------------------------
200 ! Build the ATHENA network from the parsed JSON records
201 !--------------------------------------------------------------------------
202 12 has_gnn = parsed%num_meta .gt. 0
203
204
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if(has_gnn)then
205 call build_network_from_json_gnn( &
206 network, parsed%nodes, parsed%num_nodes, &
207 parsed%inits, parsed%num_inits, &
208 parsed%inputs, parsed%num_inputs, &
209 parsed%outputs, parsed%num_outputs, &
210 parsed%meta_keys, parsed%meta_values, parsed%num_meta, &
211 6 verbose_)
212 else
213 call build_network_from_json_standard( &
214 network, parsed%nodes, parsed%num_nodes, &
215 parsed%inits, parsed%num_inits, &
216 6 parsed%inputs, parsed%num_inputs, verbose_)
217 end if
218
219
45/70
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 12 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 12 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 12 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 12 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 12 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 12 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 6000 times.
✓ Branch 33 taken 12 times.
✓ Branch 34 taken 342 times.
✓ Branch 35 taken 5658 times.
✓ Branch 36 taken 430 times.
✓ Branch 37 taken 5570 times.
✓ Branch 38 taken 233 times.
✓ Branch 39 taken 5767 times.
✓ Branch 40 taken 233 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 266 times.
✓ Branch 43 taken 233 times.
✓ Branch 44 taken 266 times.
✗ Branch 45 not taken.
✓ Branch 46 taken 266 times.
✗ Branch 47 not taken.
✓ Branch 48 taken 266 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 6000 times.
✓ Branch 51 taken 12 times.
✓ Branch 52 taken 47 times.
✓ Branch 53 taken 5953 times.
✓ Branch 54 taken 47 times.
✓ Branch 55 taken 5953 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 6000 times.
✓ Branch 58 taken 6000 times.
✓ Branch 59 taken 12 times.
✓ Branch 60 taken 22 times.
✓ Branch 61 taken 5978 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 6000 times.
✓ Branch 64 taken 6000 times.
✓ Branch 65 taken 12 times.
✓ Branch 66 taken 12 times.
✓ Branch 67 taken 5988 times.
✗ Branch 68 not taken.
✓ Branch 69 taken 6000 times.
24314 end function read_onnx
220 !###############################################################################
221
222
223 !###############################################################################
224
7/24
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 12 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 12 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 12 times.
12 subroutine initialise_json_parser(parser)
225 !! Initialise the reusable parser state objects.
226 implicit none
227
228 ! Arguments
229 type(json_parser_state_type), intent(out) :: parser
230 !! Parser state container to initialise
231
232 12 parser%section = ''
233 12 call reset_node_state(parser%node)
234 12 call reset_initialiser_state(parser%initialiser)
235 12 call reset_tensor_state(parser%input_tensor)
236 12 call reset_tensor_state(parser%output_tensor)
237
238 12 end subroutine initialise_json_parser
239 !###############################################################################
240
241
242 !###############################################################################
243 3939 subroutine detect_json_section(line, parser)
244 !! Detect the active top-level graph section.
245 implicit none
246
247 ! Arguments
248 character(*), intent(in) :: line
249 !! Current trimmed JSON line
250 type(json_parser_state_type), intent(inout) :: parser
251 !! Parser state with mutable active section
252
253 3683 if(len_trim(parser%section) .gt. 0) return
254
255
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 244 times.
256 if(index(line, '"node"') .gt. 0 .and. index(line, '[') .gt. 0)then
256 12 parser%section = 'node'
257 12 return
258 end if
259
260
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 232 times.
244 if(index(line, '"initializer"') .gt. 0 .and. index(line, '[') .gt. 0)then
261 12 parser%section = 'initializer'
262 12 return
263 end if
264
265
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 220 times.
232 if(index(line, '"input"') .gt. 0 .and. index(line, '[') .gt. 0)then
266 12 parser%section = 'input'
267 12 return
268 end if
269
270
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 208 times.
220 if(index(line, '"output"') .gt. 0 .and. index(line, '[') .gt. 0)then
271 12 parser%section = 'output'
272 12 return
273 end if
274
275
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 200 times.
208 if(index(line, '"metadataProps"') .gt. 0 .and. index(line, '[') .gt. 0)then
276 8 parser%section = 'metadata'
277 end if
278
279
2/2
✓ Branch 0 taken 3683 times.
✓ Branch 1 taken 256 times.
3939 end subroutine detect_json_section
280 !###############################################################################
281
282
283 !###############################################################################
284 subroutine parse_node_section_line(line, state, parsed, section)
285 !! Parse one line from the node section.
286 implicit none
287
288 ! Arguments
289 character(*), intent(in) :: line
290 !! Current JSON line to parse
291 type(json_node_state_type), intent(inout) :: state
292 !! Mutable node parser state
293 type(json_parse_result_type), intent(inout) :: parsed
294 !! Parsed ONNX content accumulated so far
295 character(32), intent(inout) :: section
296 !! Current top-level JSON section name
297
298
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 2319 times.
2749 if(.not.state%in_object .and. is_json_object_start(line))then
299 430 call reset_node_state(state)
300 430 state%in_object = .true.
301 430 return
302 end if
303
304
2/2
✓ Branch 0 taken 2295 times.
✓ Branch 1 taken 24 times.
2319 if(state%in_object)then
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2295 times.
2295 if(len_trim(state%active_string_array) .gt. 0)then
306 if(index(line, ']') .gt. 0) state%active_string_array = ''
307 call append_json_string_array_item(line, state%active_string_array, &
308 state%inputs, state%num_inputs, state%outputs, &
309 state%num_outputs)
310 return
311 end if
312
313
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2295 times.
2295 if(state%in_attribute)then
314 call update_array_bracket_depth(line, state%attribute_bracket_depth)
315 if(state%attribute_bracket_depth .le. 0)then
316 state%in_attribute = .false.
317 end if
318 if(index(line, '{') .gt. 0)then
319 call parse_json_attribute(line, state%attrs, state%num_attrs)
320 end if
321 return
322 end if
323
324
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 2062 times.
2295 if(index(line, '"attribute"') .gt. 0 .and. index(line, '[') .gt. 0)then
325 233 state%in_attribute = .true.
326 233 state%attribute_bracket_depth = 1
327
1/2
✓ Branch 0 taken 233 times.
✗ Branch 1 not taken.
233 if(index(line, ']') .gt. 0)then
328 233 call parse_json_attribute(line, state%attrs, state%num_attrs)
329 233 state%in_attribute = .false.
330 233 state%attribute_bracket_depth = 0
331 end if
332 233 return
333 end if
334
335
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 1632 times.
2062 if(index(line, '}') .gt. 0 .and. index(line, '"') .eq. 0 .and. &
336 .not.state%in_attribute)then
337 430 call store_node_state(state, parsed)
338 430 state%in_object = .false.
339 430 return
340 end if
341
342
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 1290 times.
1632 if(index(line, '"input"') .gt. 0)then
343 call parse_json_string_array(line, '"input"', state%inputs, &
344 342 state%num_inputs)
345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 342 times.
342 if(index(line, '[') .gt. 0 .and. index(line, ']') .eq. 0)then
346 state%active_string_array = 'input'
347 end if
348 342 return
349 end if
350
351
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 860 times.
1290 if(index(line, '"output"') .gt. 0)then
352 call parse_json_string_array(line, '"output"', state%outputs, &
353 430 state%num_outputs)
354
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
430 if(index(line, '[') .gt. 0 .and. index(line, ']') .eq. 0)then
355 state%active_string_array = 'output'
356 end if
357 430 return
358 end if
359
360
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 430 times.
860 if(index(line, '"name"') .gt. 0)then
361 430 call extract_json_string(line, '"name"', state%name)
362 430 return
363 end if
364
365
1/2
✓ Branch 0 taken 430 times.
✗ Branch 1 not taken.
430 if(index(line, '"opType"') .gt. 0)then
366 430 call extract_json_string(line, '"opType"', state%op_type)
367 430 return
368 end if
369 end if
370
371
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 if(index(line, ']') .gt. 0 .and. .not.state%in_object) section = ''
372
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2749 times.
2749 end subroutine parse_node_section_line
374 !###############################################################################
375
376
377 !###############################################################################
378 430 subroutine store_node_state(state, parsed)
379 !! Copy the current node state into the parsed result collection.
380 implicit none
381
382 ! Arguments
383 type(json_node_state_type), intent(in) :: state
384 !! Completed node parser state
385 type(json_parse_result_type), intent(inout) :: parsed
386 !! Parsed ONNX content accumulated so far
387
388 430 parsed%num_nodes = parsed%num_nodes + 1
389
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 430 times.
430 parsed%nodes(parsed%num_nodes)%name = state%name
390
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 430 times.
430 parsed%nodes(parsed%num_nodes)%op_type = state%op_type
391
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 430 times.
430 parsed%nodes(parsed%num_nodes)%num_inputs = state%num_inputs
392
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 430 times.
430 parsed%nodes(parsed%num_nodes)%num_outputs = state%num_outputs
393
394
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 88 times.
430 if(state%num_inputs .gt. 0)then
395
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 342 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 342 times.
✓ Branch 6 taken 342 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 342 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 342 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 342 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 342 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 342 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 342 times.
342 allocate(parsed%nodes(parsed%num_nodes)%inputs(state%num_inputs))
396
10/20
✗ Branch 0 not taken.
✓ Branch 1 taken 342 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 342 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 342 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 342 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 342 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 342 times.
✓ Branch 18 taken 342 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 342 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 594 times.
✓ Branch 25 taken 342 times.
936 parsed%nodes(parsed%num_nodes)%inputs = state%inputs(1:state%num_inputs)
397 end if
398
399
1/2
✓ Branch 0 taken 430 times.
✗ Branch 1 not taken.
430 if(state%num_outputs .gt. 0)then
400
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 430 times.
✓ Branch 6 taken 430 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 430 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 430 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 430 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 430 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 430 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 430 times.
430 allocate(parsed%nodes(parsed%num_nodes)%outputs(state%num_outputs))
401
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 430 times.
430 parsed%nodes(parsed%num_nodes)%outputs = &
402
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 430 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 430 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 430 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 430 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 430 times.
✓ Branch 14 taken 430 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 430 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 430 times.
✓ Branch 21 taken 430 times.
1290 state%outputs(1:state%num_outputs)
403 end if
404
405
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 197 times.
430 if(state%num_attrs .gt. 0)then
406
21/40
✗ Branch 0 not taken.
✓ Branch 1 taken 233 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 233 times.
✓ Branch 6 taken 233 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 233 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 233 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 233 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 233 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 233 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 233 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 233 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 233 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 233 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 233 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 233 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 233 times.
✓ Branch 41 taken 266 times.
✓ Branch 42 taken 233 times.
✓ Branch 43 taken 266 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 46 taken 266 times.
✗ Branch 47 not taken.
✓ Branch 48 taken 266 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 266 times.
499 allocate(parsed%nodes(parsed%num_nodes)%attributes(state%num_attrs))
407
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 233 times.
233 parsed%nodes(parsed%num_nodes)%attributes = &
408
17/52
✗ Branch 0 not taken.
✓ Branch 1 taken 233 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 233 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 233 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 233 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 233 times.
✓ Branch 14 taken 233 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 233 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 266 times.
✓ Branch 39 taken 233 times.
✓ Branch 40 taken 266 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 266 times.
✗ Branch 43 not taken.
✓ Branch 44 taken 266 times.
✗ Branch 45 not taken.
✓ Branch 46 taken 266 times.
✗ Branch 47 not taken.
✓ Branch 48 taken 266 times.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✓ Branch 51 taken 266 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 266 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 266 times.
732 state%attrs(1:state%num_attrs)
409 end if
410
411 430 end subroutine store_node_state
412 !###############################################################################
413
414
415 !###############################################################################
416 442 subroutine reset_node_state(state)
417 !! Reset the reusable node parser state.
418 implicit none
419
420 ! Arguments
421 type(json_node_state_type), intent(inout) :: state
422 !! Node parser state to reset
423
424 442 state%in_object = .false.
425 442 state%in_attribute = .false.
426 442 state%attribute_bracket_depth = 0
427 442 state%name = ''
428 442 state%op_type = ''
429 442 state%num_inputs = 0
430 442 state%num_outputs = 0
431 442 state%active_string_array = ''
432 442 state%num_attrs = 0
433
434
5/8
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 430 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
442 if(.not.allocated(state%inputs)) allocate(state%inputs(100))
435
5/8
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 430 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 12 times.
442 if(.not.allocated(state%outputs)) allocate(state%outputs(100))
436
9/14
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 430 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
✓ Branch 5 taken 430 times.
✓ Branch 6 taken 266 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 266 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 266 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 430 times.
1138 if(allocated(state%attrs)) deallocate(state%attrs)
437
8/24
✗ Branch 0 not taken.
✓ Branch 1 taken 442 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 442 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 442 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 442 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 442 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 442 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 442 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 442 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
442 allocate(state%attrs(0))
438
439 442 end subroutine reset_node_state
440 !###############################################################################
441
442
443 !###############################################################################
444 subroutine append_json_string_array_item(line, active_array, inputs, &
445 num_inputs, outputs, num_outputs)
446 !! Append one string element from a multiline JSON string array.
447 implicit none
448
449 ! Arguments
450 character(*), intent(in) :: line
451 !! Current JSON line inside a multiline string array
452 character(*), intent(in) :: active_array
453 !! Array currently being accumulated: input or output
454 character(128), intent(inout) :: inputs(:), outputs(:)
455 !! Mutable node input/output buffers
456 integer, intent(inout) :: num_inputs, num_outputs
457 !! Counts of valid input/output entries
458
459 ! Local variables
460 integer :: pos1, pos2
461 !! Quote positions used to slice the current string token
462 character(128) :: value
463 !! Current array element value
464
465 if(index(line, '"') .eq. 0) return
466
467 pos1 = index(line, '"')
468 if(pos1 .le. 0) return
469 pos2 = index(line(pos1+1:), '"')
470 if(pos2 .le. 0) return
471
472 value = line(pos1+1:pos1+pos2-1)
473
474 select case(trim(active_array))
475 case('input')
476 num_inputs = num_inputs + 1
477 inputs(num_inputs) = trim(value)
478 case('output')
479 num_outputs = num_outputs + 1
480 outputs(num_outputs) = trim(value)
481 end select
482
483 end subroutine append_json_string_array_item
484 !###############################################################################
485
486
487 !###############################################################################
488 subroutine update_array_bracket_depth(line, depth)
489 !! Update square-bracket nesting depth for multiline JSON arrays.
490 implicit none
491
492 ! Arguments
493 character(*), intent(in) :: line
494 !! Current JSON line inside a multiline array
495 integer, intent(inout) :: depth
496 !! Mutable array nesting depth
497
498 ! Local variables
499 integer :: i
500 !! Character index while scanning brackets
501
502 do i = 1, len_trim(line)
503 if(line(i:i) .eq. '[') depth = depth + 1
504 if(line(i:i) .eq. ']') depth = depth - 1
505 end do
506
507 end subroutine update_array_bracket_depth
508 !###############################################################################
509
510
511 !###############################################################################
512 subroutine parse_initialiser_section_line(line, state, parsed, section)
513 !! Parse one line from the initialiser section.
514 implicit none
515
516 ! Arguments
517 character(*), intent(in) :: line
518 !! Current JSON line to parse
519 type(json_initialiser_state_type), intent(inout) :: state
520 !! Mutable parser state for the active initialiser object
521 type(json_parse_result_type), intent(inout) :: parsed
522 !! Parsed ONNX content accumulated so far
523 character(32), intent(inout) :: section
524 !! Current top-level JSON section name
525
526 ! Local variables
527 integer :: pos, pos2
528 !! Temporary string positions used to slice the rawData field
529
530
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 259 times.
306 if(.not.state%in_object .and. is_json_object_start(line))then
531 47 call reset_initialiser_state(state)
532 47 state%in_object = .true.
533 47 return
534 end if
535
536
2/2
✓ Branch 0 taken 235 times.
✓ Branch 1 taken 24 times.
259 if(state%in_object)then
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
235 if(state%in_dims_array)then
538 call append_json_int_string_item(line, state%dims)
539 if(index(line, ']') .gt. 0) state%in_dims_array = .false.
540 return
541 end if
542
543
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 188 times.
235 if(index(line, '}') .gt. 0 .and. index(line, '"rawData"') .eq. 0 .and. &
544 index(line, '"dims"') .eq. 0)then
545 47 call store_initialiser_state(state, parsed)
546 47 state%in_object = .false.
547 47 return
548 end if
549
550
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 141 times.
188 if(index(line, '"dims"') .gt. 0)then
551 47 call parse_json_int_array_from_strings(line, state%dims)
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if(index(line, '[') .gt. 0 .and. index(line, ']') .eq. 0)then
553 state%in_dims_array = .true.
554 end if
555 47 return
556 end if
557
558
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 94 times.
141 if(index(line, '"dataType"') .gt. 0)then
559 47 call extract_json_int(line, '"dataType"', state%data_type)
560 47 return
561 end if
562
563
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 47 times.
94 if(index(line, '"name"') .gt. 0)then
564 47 call extract_json_string(line, '"name"', state%name)
565 47 return
566 end if
567
568
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if(index(line, '"rawData"') .gt. 0)then
569 47 pos = index(line, '"rawData"') + 9
570
2/4
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
47 pos2 = index(line(pos:), '"')
571
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if(pos2 .gt. 0)then
572 47 pos = pos + pos2
573
2/4
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
47 pos2 = index(line(pos:), '"')
574
9/18
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47 times.
✓ Branch 7 taken 47 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 47 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 47 times.
✓ Branch 14 taken 47 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 47 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 47 times.
47 if(pos2 .gt. 0) state%raw_data = line(pos:pos+pos2-2)
575 end if
576 47 return
577 end if
578 end if
579
580
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 if(index(line, ']') .gt. 0 .and. .not.state%in_object) section = ''
581
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 306 times.
306 end subroutine parse_initialiser_section_line
583 !###############################################################################
584
585
586 !###############################################################################
587 47 subroutine store_initialiser_state(state, parsed)
588 !! Copy the current initialiser state into the parsed result collection.
589 use athena__onnx_utils, only: decode_base64_to_float32, &
590 decode_base64_to_int64
591 implicit none
592
593 ! Arguments
594 type(json_initialiser_state_type), intent(in) :: state
595 !! Completed initialiser parse state to copy into the result object
596 type(json_parse_result_type), intent(inout) :: parsed
597 !! Parsed ONNX content accumulated so far
598
599 ! Local variables
600 integer :: j, n_decoded
601 !! Integer loop index and decoded tensor length
602 47 real(real32), allocatable :: decoded_floats(:)
603 !! Float payload decoded from base64 rawData
604 47 integer, allocatable :: decoded_ints(:)
605 !! Int64 payload decoded from base64 rawData
606
607 47 parsed%num_inits = parsed%num_inits + 1
608
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
47 parsed%inits(parsed%num_inits)%name = state%name
609
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
47 parsed%inits(parsed%num_inits)%data_type = state%data_type
610
611
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if(allocated(state%dims))then
612
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✓ Branch 6 taken 47 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 47 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 47 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 47 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 47 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 47 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 47 times.
47 allocate(parsed%inits(parsed%num_inits)%dims(size(state%dims)))
613
10/24
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 47 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 47 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 47 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 47 times.
✓ Branch 18 taken 47 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 47 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 92 times.
✓ Branch 29 taken 47 times.
139 parsed%inits(parsed%num_inits)%dims = state%dims
614 end if
615
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if(len_trim(state%raw_data) .eq. 0) return
617
618
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 if(state%data_type .eq. 1)then
619 call decode_base64_to_float32(trim(state%raw_data), decoded_floats, &
620
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
47 n_decoded)
621
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✓ Branch 6 taken 47 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 47 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 47 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 47 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 47 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 47 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 47 times.
47 allocate(parsed%inits(parsed%num_inits)%data(n_decoded))
622
10/24
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 47 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 47 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 47 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 47 times.
✓ Branch 18 taken 47 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 47 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 3212 times.
✓ Branch 29 taken 47 times.
3259 parsed%inits(parsed%num_inits)%data = decoded_floats
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 deallocate(decoded_floats)
624 else if(state%data_type .eq. 7)then
625 call decode_base64_to_int64(trim(state%raw_data), decoded_ints, &
626 n_decoded)
627 allocate(parsed%inits(parsed%num_inits)%data(n_decoded))
628 do j = 1, n_decoded
629 parsed%inits(parsed%num_inits)%data(j) = &
630 real(decoded_ints(j), real32)
631 end do
632 deallocate(decoded_ints)
633 end if
634
635
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
47 end subroutine store_initialiser_state
636 !###############################################################################
637
638
639 !###############################################################################
640 59 subroutine reset_initialiser_state(state)
641 !! Reset the reusable initialiser parser state.
642 implicit none
643
644 ! Arguments
645 type(json_initialiser_state_type), intent(inout) :: state
646 !! Initialiser parser state to reset
647
648 59 state%in_object = .false.
649 59 state%in_dims_array = .false.
650 59 state%name = ''
651 59 state%data_type = 1
652
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
59 if(allocated(state%dims)) deallocate(state%dims)
653
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 59 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 59 times.
59 allocate(state%dims(0))
654
3/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
59 if(allocated(state%raw_data)) deallocate(state%raw_data)
655
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 59 times.
59 allocate(character(0) :: state%raw_data)
656
657 59 end subroutine reset_initialiser_state
658 !###############################################################################
659
660
661 !###############################################################################
662 subroutine append_json_int_string_item(line, values)
663 !! Append one integer value stored as a quoted JSON string.
664 implicit none
665
666 ! Arguments
667 character(*), intent(in) :: line
668 !! Current JSON line inside a multiline integer array
669 integer, allocatable, intent(inout) :: values(:)
670 !! Mutable integer array buffer updated in-place
671
672 ! Local variables
673 integer :: pos1, pos2, parsed_value, stat
674 !! Quote positions plus parsed integer value and read status
675 character(32) :: value
676 !! Extracted numeric token before conversion
677
678 if(index(line, '"') .eq. 0) return
679
680 pos1 = index(line, '"')
681 if(pos1 .le. 0) return
682 pos2 = index(line(pos1+1:), '"')
683 if(pos2 .le. 0) return
684
685 value = line(pos1+1:pos1+pos2-1)
686 read(value, *, iostat=stat) parsed_value
687 if(stat .ne. 0) return
688
689 if(.not.allocated(values))then
690 allocate(values(1))
691 values(1) = parsed_value
692 else
693 values = [values, parsed_value]
694 end if
695
696 end subroutine append_json_int_string_item
697 !###############################################################################
698
699
700 !###############################################################################
701 660 subroutine parse_tensor_section_line(line, state, tensors, num_tensors, &
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 660 times.
660 section)
703 !! Parse one line from the input or output tensor section.
704 implicit none
705
706 ! Arguments
707 character(*), intent(in) :: line
708 !! Current JSON line to parse
709 type(json_tensor_state_type), intent(inout) :: state
710 !! Mutable tensor parser state
711 type(onnx_tensor_type), intent(inout) :: tensors(:)
712 !! Parsed tensor destination array
713 integer, intent(inout) :: num_tensors
714 !! Number of valid tensor entries in tensors
715 character(32), intent(inout) :: section
716 !! Current top-level JSON section name
717
718 ! Local variables
719 integer :: stat, dim_value
720 !! Read status and parsed dimension value
721 character(256) :: tmpstr
722 !! Temporary string buffer for dimValue parsing
723
724
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 626 times.
660 if(.not.state%in_object .and. is_json_object_start(line))then
725 34 call reset_tensor_state(state)
726 34 state%in_object = .true.
727 34 state%object_depth = 1
728 204 return
729 end if
730
731
2/2
✓ Branch 0 taken 578 times.
✓ Branch 1 taken 48 times.
626 if(state%in_object)then
732 578 call update_object_depth(line, state%object_depth)
733
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 544 times.
578 if(state%object_depth .le. 0)then
734
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 34 times.
34 call store_tensor_state(state, tensors, num_tensors)
735 34 state%in_object = .false.
736 34 return
737 end if
738
739
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 510 times.
544 if(index(line, '"name"') .gt. 0)then
740 34 call extract_json_string(line, '"name"', state%name)
741 34 return
742 end if
743
744
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 476 times.
510 if(index(line, '"elemType"') .gt. 0)then
745 34 call extract_json_int(line, '"elemType"', state%elem_type)
746 34 return
747 end if
748
749
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 439 times.
476 if(index(line, '"dimValue"') .gt. 0)then
750 37 call extract_json_string(line, '"dimValue"', tmpstr)
751 37 read(tmpstr, *, iostat=stat) dim_value
752
16/26
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 37 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 37 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 37 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 37 times.
✓ Branch 17 taken 35 times.
✓ Branch 18 taken 37 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 37 times.
✓ Branch 21 taken 72 times.
✓ Branch 22 taken 37 times.
✓ Branch 23 taken 37 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 37 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 37 times.
✓ Branch 29 taken 72 times.
✓ Branch 30 taken 37 times.
216 if(stat .eq. 0) state%dim_values = [state%dim_values, dim_value]
753 37 return
754 end if
755
756
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 408 times.
439 if(index(line, '"dimParam"') .gt. 0)then
757
15/24
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 31 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 31 times.
✓ Branch 15 taken 4 times.
✓ Branch 16 taken 31 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 31 times.
✓ Branch 19 taken 35 times.
✓ Branch 20 taken 31 times.
✓ Branch 21 taken 31 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 31 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 31 times.
✓ Branch 27 taken 35 times.
✓ Branch 28 taken 31 times.
105 state%dim_values = [state%dim_values, -1]
758 31 return
759 end if
760 end if
761
762
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 432 times.
456 if(index(line, ']') .gt. 0 .and. .not.state%in_object) section = ''
763
764
1/2
✓ Branch 0 taken 660 times.
✗ Branch 1 not taken.
660 end subroutine parse_tensor_section_line
765 !###############################################################################
766
767
768 !###############################################################################
769
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 subroutine store_tensor_state(state, tensors, num_tensors)
770 !! Copy the current tensor state into the parsed result collection.
771 implicit none
772
773 ! Arguments
774 type(json_tensor_state_type), intent(in) :: state
775 !! Completed tensor parser state
776 type(onnx_tensor_type), intent(inout) :: tensors(:)
777 !! Parsed tensor destination array
778 integer, intent(inout) :: num_tensors
779 !! Number of valid tensor entries in tensors
780
781 34 num_tensors = num_tensors + 1
782
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
34 tensors(num_tensors)%name = state%name
783
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
34 tensors(num_tensors)%elem_type = state%elem_type
784
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
✓ Branch 6 taken 34 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 34 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 34 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 34 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 34 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 34 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 34 times.
34 allocate(tensors(num_tensors)%dims(size(state%dim_values)))
785
10/24
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 34 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 34 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 34 times.
✓ Branch 18 taken 34 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 34 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 68 times.
✓ Branch 29 taken 34 times.
102 tensors(num_tensors)%dims = state%dim_values
786
787 34 end subroutine store_tensor_state
788 !###############################################################################
789
790
791 !###############################################################################
792 58 subroutine reset_tensor_state(state)
793 !! Reset the reusable tensor parser state.
794 implicit none
795
796 ! Arguments
797 type(json_tensor_state_type), intent(inout) :: state
798 !! Tensor parser state to reset
799
800 58 state%in_object = .false.
801 58 state%object_depth = 0
802 58 state%name = ''
803 58 state%elem_type = 1
804
3/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
58 if(allocated(state%dim_values)) deallocate(state%dim_values)
805
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 58 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 58 times.
58 allocate(state%dim_values(0))
806
807 58 end subroutine reset_tensor_state
808 !###############################################################################
809
810
811 !###############################################################################
812 subroutine parse_metadata_line(line, parsed, section)
813 !! Parse one metadataProps line.
814 implicit none
815
816 ! Arguments
817 character(*), intent(in) :: line
818 !! Current metadata JSON line
819 type(json_parse_result_type), intent(inout) :: parsed
820 !! Parsed ONNX content accumulated so far
821 character(32), intent(inout) :: section
822 !! Current top-level JSON section name
823
824
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
24 if(index(line, '"key"') .gt. 0 .and. index(line, '"value"') .gt. 0)then
825 8 parsed%num_meta = parsed%num_meta + 1
826 call extract_json_string(line, '"key"', &
827
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 parsed%meta_keys(parsed%num_meta))
828 call extract_json_string(line, '"value"', &
829
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 parsed%meta_values(parsed%num_meta))
830 end if
831
832
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
24 if(index(line, ']') .gt. 0) section = ''
833
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 end subroutine parse_metadata_line
835 !###############################################################################
836
837
838 !###############################################################################
839 3715 function is_json_object_start(line) result(is_start)
840 !! Return true for section object lines like `{`.
841 implicit none
842
843 ! Arguments
844 character(*), intent(in) :: line
845 !! Current JSON line to classify
846
847 logical :: is_start
848 !! Whether this line is the start of a JSON object
849
850 is_start = index(line, '{') .gt. 0 .and. &
851 3715 index(line, '"') .eq. 0
852
853 3715 end function is_json_object_start
854 !###############################################################################
855
856
857 !###############################################################################
858 578 subroutine update_object_depth(line, object_depth)
859 !! Update a nested object depth counter from one JSON line.
860 implicit none
861
862 ! Arguments
863 character(*), intent(in) :: line
864 !! Current JSON line
865 integer, intent(inout) :: object_depth
866 !! Mutable object depth counter
867
868 ! Local variables
869 integer :: i
870 !! Character index while scanning braces
871
872
2/2
✓ Branch 0 taken 4397 times.
✓ Branch 1 taken 578 times.
4975 do i = 1, len_trim(line)
873
6/10
✓ Branch 0 taken 4397 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4397 times.
✓ Branch 5 taken 4397 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4397 times.
✓ Branch 10 taken 170 times.
✓ Branch 11 taken 4227 times.
4397 if(line(i:i) .eq. '{') object_depth = object_depth + 1
874
6/10
✓ Branch 0 taken 4397 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4397 times.
✓ Branch 5 taken 4397 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4397 times.
✓ Branch 10 taken 204 times.
✓ Branch 11 taken 4193 times.
4975 if(line(i:i) .eq. '}') object_depth = object_depth - 1
875 end do
876
877 578 end subroutine update_object_depth
878 !###############################################################################
879
880
881 !###############################################################################
882 6 subroutine build_network_from_json_gnn( &
883 12 network, nodes, num_nodes, inits, num_inits, &
884 inputs, num_inputs, outputs, num_outputs, &
885
5/10
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
18 meta_keys, meta_values, num_meta, verbose_)
886 !! Build a network containing GNN layers from parsed JSON data.
887 !!
888 !! Metadata layer creation is delegated to the registered creator in
889 !! list_of_onnx_meta_layer_creators, keyed by the subtype stored in
890 !! the metadata value string.
891 !! Standard (non-GNN) layer creation is delegated to the registered
892 !! creator in list_of_onnx_layer_creators, keyed by the ONNX op_type.
893 use athena__base_layer, only: base_layer_type
894 use athena__container_layer, only: list_of_onnx_meta_layer_creators, &
895 allocate_list_of_onnx_meta_layer_creators, &
896 list_of_onnx_layer_creators, &
897 allocate_list_of_onnx_layer_creators
898 implicit none
899
900 ! Arguments
901 type(network_type), intent(inout) :: network
902 !! Network to populate from parsed ONNX content
903 type(onnx_node_type), intent(in) :: nodes(:)
904 !! Parsed ONNX nodes
905 integer, intent(in) :: num_nodes
906 !! Number of valid entries in nodes
907 type(onnx_initialiser_type), intent(in) :: inits(:)
908 !! Parsed ONNX initialisers
909 integer, intent(in) :: num_inits
910 !! Number of valid entries in inits
911 type(onnx_tensor_type), intent(in) :: inputs(:)
912 !! Parsed graph input tensors
913 integer, intent(in) :: num_inputs
914 !! Number of valid entries in inputs
915 type(onnx_tensor_type), intent(in) :: outputs(:)
916 !! Parsed graph output tensors
917 integer, intent(in) :: num_outputs
918 !! Number of valid entries in outputs
919 character(256), intent(in) :: meta_keys(:), meta_values(:)
920 !! Metadata keys and values from metadataProps
921 integer, intent(in) :: num_meta
922 !! Number of valid metadata entries
923 integer, intent(in) :: verbose_
924 !! Effective verbosity level
925
926 ! Local variables
927
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 integer, allocatable :: ordered_layer_ids(:)
928 !! Sorted unique layer ids discovered from metadata and node names
929 integer :: i, layer_id, meta_index, node_index
930 !! Loop index and per-layer lookup indices
931
932
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if(.not.allocated(list_of_onnx_meta_layer_creators))then
933 3 call allocate_list_of_onnx_meta_layer_creators()
934 end if
935
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if(.not.allocated(list_of_onnx_layer_creators))then
936 3 call allocate_list_of_onnx_layer_creators()
937 end if
938
939
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 allocate(ordered_layer_ids(0))
940
941
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 do i = 1, num_meta
942 call append_unique_layer_id_from_meta_key( &
943
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
14 meta_keys(i), ordered_layer_ids)
944 end do
945
946
2/2
✓ Branch 0 taken 201 times.
✓ Branch 1 taken 6 times.
207 do i = 1, num_nodes
947
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 201 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 201 times.
207 call append_unique_primary_layer_id(nodes(i)%name, ordered_layer_ids)
948 end do
949
950 6 call sort_int_array(ordered_layer_ids)
951
952
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 do i = 1, size(ordered_layer_ids)
953
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 layer_id = ordered_layer_ids(i)
954
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
8 meta_index = find_metadata_for_layer_id(meta_keys, num_meta, layer_id)
955
956
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if(meta_index .gt. 0)then
957 call add_gnn_layer_from_metadata( &
958
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
16 network, meta_keys(meta_index), meta_values(meta_index), &
959
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
24 inits, num_inits, verbose_)
960 8 cycle
961 end if
962
963 node_index = find_primary_node_for_layer_id(nodes, num_nodes, layer_id)
964 if(node_index .le. 0) cycle
965
966 call add_standard_layer_from_onnx( &
967 network, layer_id, node_index, nodes, num_nodes, &
968
0/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
6 inits, num_inits, verbose_)
969 end do
970
971
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if(allocated(ordered_layer_ids)) deallocate(ordered_layer_ids)
972
973
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(verbose_ .gt. 0)then
974 write(*,*) 'Network built with ', network%num_layers, ' layers'
975 end if
976
977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 end subroutine build_network_from_json_gnn
978 !###############################################################################
979
980
981 !###############################################################################
982 8 subroutine add_gnn_layer_from_metadata(network, meta_key, meta_value, inits, &
983 num_inits, verbose_)
984 !! Create one GNN or NOP layer from metadata and append it to the network.
985 use athena__base_layer, only: base_layer_type
986 use athena__container_layer, only: list_of_onnx_meta_layer_creators
987 implicit none
988
989 ! Arguments
990 type(network_type), intent(inout) :: network
991 !! Network receiving the created layer
992 character(*), intent(in) :: meta_key, meta_value
993 !! Metadata key/value pair describing one layer
994 type(onnx_initialiser_type), intent(in) :: inits(:)
995 !! Parsed ONNX initialisers
996 integer, intent(in) :: num_inits, verbose_
997 !! Number of initialisers and effective verbosity level
998
999 ! Local variables
1000 character(64) :: subtype_name
1001 !! Parsed subtype token from metadata payload
1002 integer :: i, layer_index
1003 !! Creator search index and selected creator slot
1004
1005 8 call extract_gnn_subtype(meta_value, subtype_name)
1006
1007 8 layer_index = 0
1008
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 do i = 1, size(list_of_onnx_meta_layer_creators)
1009
6/10
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25 times.
✓ Branch 8 taken 25 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 25 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✓ Branch 13 taken 17 times.
25 if(trim(list_of_onnx_meta_layer_creators(i)%layer_subtype) .eq. &
1010 trim(subtype_name))then
1011 8 layer_index = i
1012 8 exit
1013 end if
1014 end do
1015
1016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(layer_index .eq. 0)then
1017 if(index(trim(meta_key), 'athena_nop_') .gt. 0)then
1018 write(*,*) 'ERROR: Unknown NOP subtype: ', trim(subtype_name)
1019 else
1020 write(*,*) 'ERROR: Unknown GNN subtype: ', trim(subtype_name)
1021 end if
1022 return
1023 end if
1024
1025 24 block
1026 24 class(base_layer_type), allocatable :: meta_layer
1027
1028 meta_layer = list_of_onnx_meta_layer_creators(layer_index)%create_ptr( &
1029
8/20
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 8 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✓ Branch 23 taken 8 times.
✗ Branch 24 not taken.
✓ Branch 26 taken 8 times.
✗ Branch 27 not taken.
8 meta_key, meta_value, inits(1:num_inits), verbose_)
1030
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
32 call network%add(meta_layer)
1031 end block
1032
1033
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 end subroutine add_gnn_layer_from_metadata
1034 !###############################################################################
1035
1036
1037 !###############################################################################
1038 subroutine add_standard_layer_from_onnx( &
1039 network, layer_id, node_index, nodes, num_nodes, &
1040 inits, num_inits, verbose_)
1041 !! Create standard (non-GNN) layers for a given layer_id using the
1042 !! registered ONNX creator framework (list_of_onnx_layer_creators).
1043 !!
1044 !! Processes the primary node and any trailing activation node.
1045 use athena__base_layer, only: base_layer_type
1046 use athena__container_layer, only: list_of_onnx_layer_creators
1047 use athena__onnx_utils, only: row_to_col_major_2d
1048 implicit none
1049
1050 ! Arguments
1051 type(network_type), intent(inout) :: network
1052 !! Network receiving the created layer(s)
1053 integer, intent(in) :: layer_id, node_index, num_nodes, num_inits
1054 !! Layer id, primary node index, node count and initialiser count
1055 integer, intent(in) :: verbose_
1056 !! Effective verbosity level
1057 type(onnx_node_type), intent(in) :: nodes(:)
1058 !! Parsed ONNX nodes
1059 type(onnx_initialiser_type), intent(in) :: inits(:)
1060 !! Parsed ONNX initialisers
1061
1062 ! Local variables
1063 integer :: j, k, layer_index, actv_index, ndims, num_matching
1064 !! Loop indices and creator/shape lookup values
1065 character(128) :: op_type_name, out_name
1066 !! Current ONNX op_type and output tensor name
1067 type(onnx_initialiser_type), allocatable :: init_list(:)
1068 !! Initialisers matched to the active node inputs
1069 type(onnx_tensor_type), allocatable :: value_info_list(:)
1070 !! Synthetic output shape hints passed to creator
1071 class(base_layer_type), allocatable :: layer
1072 !! Created ATHENA layer instance
1073
1074 op_type_name = trim(adjustl(nodes(node_index)%op_type))
1075
1076 layer_index = findloc( &
1077 [ list_of_onnx_layer_creators(:)%op_type ], &
1078 trim(op_type_name), dim = 1)
1079
1080 if(layer_index .eq. 0)then
1081 if(verbose_ .gt. 0)then
1082 write(*,*) 'Skipping unsupported ONNX node in GNN import: ', &
1083 trim(nodes(node_index)%name), ' op=', trim(op_type_name)
1084 end if
1085 return
1086 end if
1087
1088 num_matching = 0
1089 if(allocated(nodes(node_index)%inputs))then
1090 do j = 1, size(nodes(node_index)%inputs)
1091 do k = 1, num_inits
1092 if(trim(nodes(node_index)%inputs(j)) .eq. &
1093 trim(inits(k)%name))then
1094 num_matching = num_matching + 1
1095 end if
1096 end do
1097 end do
1098 end if
1099
1100 allocate(init_list(num_matching))
1101 num_matching = 0
1102 if(allocated(nodes(node_index)%inputs))then
1103 do j = 1, size(nodes(node_index)%inputs)
1104 do k = 1, num_inits
1105 if(trim(nodes(node_index)%inputs(j)) .ne. &
1106 trim(inits(k)%name)) cycle
1107
1108 num_matching = num_matching + 1
1109 init_list(num_matching)%name = inits(k)%name
1110 init_list(num_matching)%data_type = inits(k)%data_type
1111
1112 if(allocated(inits(k)%dims))then
1113 allocate(init_list(num_matching)%dims(size(inits(k)%dims)))
1114 init_list(num_matching)%dims = inits(k)%dims
1115 end if
1116
1117 if(allocated(inits(k)%data))then
1118 allocate(init_list(num_matching)%data(size(inits(k)%data)))
1119 if(allocated(inits(k)%dims))then
1120 if(size(inits(k)%dims) .eq. 2)then
1121 call row_to_col_major_2d( &
1122 inits(k)%data, init_list(num_matching)%data, &
1123 inits(k)%dims(1), inits(k)%dims(2))
1124 else
1125 init_list(num_matching)%data = inits(k)%data
1126 end if
1127 else
1128 init_list(num_matching)%data = inits(k)%data
1129 end if
1130 end if
1131
1132 if(allocated(inits(k)%int_data))then
1133 allocate(init_list(num_matching)%int_data(size(inits(k)%int_data)))
1134 init_list(num_matching)%int_data = inits(k)%int_data
1135 end if
1136 end do
1137 end do
1138 end if
1139
1140 allocate(value_info_list(0))
1141 if(allocated(nodes(node_index)%outputs) .and. &
1142 nodes(node_index)%num_outputs .ge. 1)then
1143 out_name = trim(nodes(node_index)%outputs(1))
1144
1145 do j = 1, size(init_list)
1146 if(.not.allocated(init_list(j)%dims)) cycle
1147 if(size(init_list(j)%dims) .lt. 2) cycle
1148 ndims = size(init_list(j)%dims)
1149
1150 block
1151 type(onnx_tensor_type) :: vi
1152
1153 vi%name = out_name
1154 vi%elem_type = 1
1155 if(trim(op_type_name) .eq. 'Conv' .and. ndims .ge. 3)then
1156 allocate(vi%dims(ndims))
1157 vi%dims(1) = 1
1158 vi%dims(2) = init_list(j)%dims(ndims)
1159 vi%dims(3:ndims) = 0
1160 else
1161 allocate(vi%dims(2))
1162 vi%dims(1) = 1
1163 vi%dims(2) = init_list(j)%dims(1)
1164 end if
1165
1166 deallocate(value_info_list)
1167 allocate(value_info_list(1))
1168 value_info_list(1)%name = vi%name
1169 value_info_list(1)%elem_type = vi%elem_type
1170 if(allocated(vi%dims))then
1171 allocate(value_info_list(1)%dims(size(vi%dims)))
1172 value_info_list(1)%dims = vi%dims
1173 end if
1174 end block
1175 exit
1176 end do
1177 end if
1178
1179 layer = list_of_onnx_layer_creators(layer_index)%create_ptr( &
1180 nodes(node_index), init_list, value_info_list, verbose=verbose_)
1181 call network%add(layer)
1182
1183 deallocate(init_list)
1184 deallocate(value_info_list)
1185
1186 actv_index = find_activation_node_for_layer_id( &
1187 nodes, num_nodes, layer_id)
1188 if(actv_index .gt. 0)then
1189 op_type_name = trim(adjustl(nodes(actv_index)%op_type))
1190 layer_index = findloc( &
1191 [ list_of_onnx_layer_creators(:)%op_type ], &
1192 trim(op_type_name), dim = 1)
1193 if(layer_index .gt. 0)then
1194 allocate(init_list(0))
1195 allocate(value_info_list(0))
1196 if(allocated(layer)) deallocate(layer)
1197 layer = list_of_onnx_layer_creators(layer_index)%create_ptr( &
1198 nodes(actv_index), init_list, value_info_list, &
1199 verbose=verbose_)
1200 call network%add(layer)
1201 deallocate(init_list)
1202 deallocate(value_info_list)
1203 end if
1204 end if
1205
1206 end subroutine add_standard_layer_from_onnx
1207 !###############################################################################
1208
1209
1210 !###############################################################################
1211 8 subroutine extract_gnn_subtype(meta_value, gnn_subtype)
1212 !! Extract the subtype=... token from one metadata value string.
1213 implicit none
1214
1215 ! Arguments
1216 character(*), intent(in) :: meta_value
1217 !! Metadata payload string
1218 character(*), intent(out) :: gnn_subtype
1219 !! Extracted subtype token
1220
1221 ! Local variables
1222 integer :: pos, pos2, k
1223 !! Token scanning positions and key delimiter index
1224 character(256) :: token, key
1225 !! Current token and token key
1226
1227
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 gnn_subtype = ''
1228 8 pos = 1
1229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 do while(pos .le. len_trim(meta_value))
1230
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 pos2 = index(meta_value(pos:), ';')
1231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(pos2 .eq. 0)then
1232 token = meta_value(pos:len_trim(meta_value))
1233 pos = len_trim(meta_value) + 1
1234 else
1235
5/10
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
8 token = meta_value(pos:pos+pos2-2)
1236 8 pos = pos + pos2
1237 end if
1238
1239 8 k = index(token, '=')
1240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(k .eq. 0) cycle
1241
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
8 key = trim(adjustl(token(1:k-1)))
1242
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if(trim(key) .eq. 'subtype')then
1243
5/10
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
8 gnn_subtype = trim(adjustl(token(k+1:)))
1244 8 return
1245 end if
1246 end do
1247
1248
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 end subroutine extract_gnn_subtype
1249 !###############################################################################
1250
1251
1252 !###############################################################################
1253 8 subroutine append_unique_layer_id_from_meta_key(meta_key, ids)
1254 !! Append a layer id parsed from athena_gnn_node_<id> or
1255 !! athena_nop_node_<id> if not already present.
1256 implicit none
1257
1258 ! Arguments
1259 character(*), intent(in) :: meta_key
1260 !! Metadata key potentially containing a layer id
1261 integer, allocatable, intent(inout) :: ids(:)
1262 !! Unique set of discovered layer ids
1263
1264 ! Local variables
1265 integer :: layer_id, pos, stat, i
1266 !! Parsed id, prefix position, read status and loop index
1267 character(128) :: rest
1268 !! Metadata suffix containing the candidate id
1269 logical :: exists
1270 !! Whether the id already exists in ids
1271
1272
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 pos = index(trim(meta_key), 'athena_gnn_node_')
1273
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
8 if(pos .eq. 0) pos = index(trim(meta_key), 'athena_nop_node_')
1274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(pos .eq. 0) return
1275
1276
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
8 rest = adjustl(trim(meta_key(pos+16:)))
1277 8 read(rest, *, iostat=stat) layer_id
1278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(stat .ne. 0) return
1279
1280 8 exists = .false.
1281
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 do i = 1, size(ids)
1282
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
11 if(ids(i) .eq. layer_id)then
1283 exists = .true.
1284 exit
1285 end if
1286 end do
1287
17/28
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 8 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 8 times.
✓ Branch 19 taken 3 times.
✓ Branch 20 taken 8 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 8 times.
✓ Branch 23 taken 11 times.
✓ Branch 24 taken 8 times.
✓ Branch 25 taken 8 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 8 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 8 times.
✓ Branch 31 taken 11 times.
✓ Branch 32 taken 8 times.
33 if(.not.exists) ids = [ids, layer_id]
1288
1289 8 end subroutine append_unique_layer_id_from_meta_key
1290 !###############################################################################
1291
1292
1293 !###############################################################################
1294 201 subroutine append_unique_primary_layer_id(node_name, ids)
1295 !! Append a layer id parsed from a primary node name node_<id>.
1296 implicit none
1297
1298 ! Arguments
1299 character(*), intent(in) :: node_name
1300 !! Node name potentially containing a primary layer id
1301 integer, allocatable, intent(inout) :: ids(:)
1302 !! Unique set of discovered layer ids
1303
1304 ! Local variables
1305 integer :: layer_id, i
1306 !! Parsed id and loop index
1307 logical :: is_primary, exists
1308 !! Primary-node flag and duplicate-id flag
1309
1310 201 call parse_primary_layer_id(node_name, layer_id, is_primary)
1311
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 4 times.
201 if(.not.is_primary) return
1312
1313 4 exists = .false.
1314
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 do i = 1, size(ids)
1315
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 if(ids(i) .eq. layer_id)then
1316 4 exists = .true.
1317 4 exit
1318 end if
1319 end do
1320
1/28
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
4 if(.not.exists) ids = [ids, layer_id]
1321
1322 201 end subroutine append_unique_primary_layer_id
1323 !###############################################################################
1324
1325
1326 !###############################################################################
1327 201 subroutine parse_primary_layer_id(node_name, layer_id, is_primary)
1328 !! Parse node_<id> names and mark true only for primary layer nodes.
1329 implicit none
1330
1331 ! Arguments
1332 character(*), intent(in) :: node_name
1333 !! Candidate ONNX node name
1334 integer, intent(out) :: layer_id
1335 !! Parsed layer id when present
1336 logical, intent(out) :: is_primary
1337 !! Whether node_name matches primary pattern node_<id>
1338
1339 ! Local variables
1340 integer :: stat
1341 !! Read status for integer parse
1342 character(128) :: rest
1343 !! Node name suffix after node_ prefix
1344
1345 201 layer_id = -1
1346 201 is_primary = .false.
1347
1348
2/4
✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 201 times.
201 if(index(trim(node_name), 'node_') .ne. 1) return
1349
2/4
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 201 times.
✗ Branch 4 not taken.
201 rest = trim(node_name(6:))
1350
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 4 times.
201 if(index(rest, '_') .gt. 0) return
1351
1352 4 read(rest, *, iostat=stat) layer_id
1353
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(stat .eq. 0 .and. layer_id .gt. 0) is_primary = .true.
1354
1355 201 end subroutine parse_primary_layer_id
1356 !###############################################################################
1357
1358
1359 !###############################################################################
1360
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 function find_metadata_for_layer_id(meta_keys, num_meta, layer_id) result(meta_index)
1361 !! Return metadata index for a given layer id, or 0 if absent.
1362 implicit none
1363
1364 ! Arguments
1365 character(256), intent(in) :: meta_keys(:)
1366 !! Metadata keys list
1367 integer, intent(in) :: num_meta, layer_id
1368 !! Number of metadata entries and target layer id
1369
1370 integer :: meta_index
1371 !! Index of the found metadata entry, or 0 if not found
1372
1373 ! Local variables
1374 integer :: i, id_tmp
1375 !! Loop index and parsed id candidate
1376 logical :: found
1377 !! Whether a key parsed successfully
1378
1379 8 meta_index = 0
1380
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 do i = 1, num_meta
1381
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 call parse_meta_layer_id(meta_keys(i), id_tmp, found)
1382
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 3 times.
11 if(found .and. id_tmp .eq. layer_id)then
1383 8 meta_index = i
1384 8 return
1385 end if
1386 end do
1387
1388 end function find_metadata_for_layer_id
1389 !###############################################################################
1390
1391
1392 !###############################################################################
1393 11 subroutine parse_meta_layer_id(meta_key, layer_id, found)
1394 !! Parse athena_gnn_node_<id> or athena_nop_node_<id> metadata key layer id.
1395 implicit none
1396
1397 ! Arguments
1398 character(*), intent(in) :: meta_key
1399 !! Metadata key potentially containing a layer id
1400 integer, intent(out) :: layer_id
1401 !! Parsed layer id value
1402 logical, intent(out) :: found
1403 !! Whether parsing succeeded
1404
1405 ! Local variables
1406 integer :: pos, stat
1407 !! Prefix position and read status
1408 character(128) :: rest
1409 !! Metadata suffix containing the candidate id
1410
1411 11 layer_id = -1
1412 11 found = .false.
1413
1414
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 pos = index(trim(meta_key), 'athena_gnn_node_')
1415
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 4 times.
11 if(pos .gt. 0)then
1416
4/8
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 7 times.
✗ Branch 10 not taken.
7 rest = adjustl(trim(meta_key(pos+16:)))
1417 7 read(rest, *, iostat=stat) layer_id
1418
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(stat .eq. 0 .and. layer_id .gt. 0) found = .true.
1419 11 return
1420 end if
1421
1422
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 pos = index(trim(meta_key), 'athena_nop_node_')
1423
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(pos .gt. 0)then
1424
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
4 rest = adjustl(trim(meta_key(pos+16:)))
1425 4 read(rest, *, iostat=stat) layer_id
1426
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(stat .eq. 0 .and. layer_id .gt. 0) found = .true.
1427 4 return
1428 end if
1429
1430 11 end subroutine parse_meta_layer_id
1431 !###############################################################################
1432
1433
1434 !###############################################################################
1435 function find_primary_node_for_layer_id(nodes, num_nodes, layer_id) &
1436 result(node_index)
1437 !! Return node index for primary node_<id>, or 0 if not found.
1438 implicit none
1439
1440 ! Arguments
1441 type(onnx_node_type), intent(in) :: nodes(:)
1442 !! Parsed ONNX nodes
1443 integer, intent(in) :: num_nodes, layer_id
1444 !! Number of valid nodes and target layer id
1445
1446 integer :: node_index
1447 !! Index of the found primary node, or 0 if not found
1448
1449 ! Local variables
1450 integer :: i, id_tmp
1451 !! Loop index and parsed node id candidate
1452 logical :: is_primary
1453 !! Whether current node matches primary pattern
1454
1455 node_index = 0
1456 do i = 1, num_nodes
1457 call parse_primary_layer_id(nodes(i)%name, id_tmp, is_primary)
1458 if(is_primary .and. id_tmp .eq. layer_id)then
1459 node_index = i
1460 return
1461 end if
1462 end do
1463
1464 end function find_primary_node_for_layer_id
1465 !###############################################################################
1466
1467
1468 !###############################################################################
1469 function find_activation_node_for_layer_id(nodes, num_nodes, layer_id) &
1470 result(actv_index)
1471 !! Return node index for activation attached to node_<id>, or 0.
1472 implicit none
1473
1474 ! Arguments
1475 type(onnx_node_type), intent(in) :: nodes(:)
1476 !! Parsed ONNX nodes
1477 integer, intent(in) :: num_nodes, layer_id
1478 !! Number of valid nodes and target layer id
1479
1480 integer :: actv_index
1481 !! Index of the found activation node
1482
1483 ! Local variables
1484 integer :: i
1485 !! Loop index
1486 character(128) :: prefix
1487 !! Prefix for activation nodes linked to layer_id
1488
1489 write(prefix, '("node_",I0,"_")') layer_id
1490 actv_index = 0
1491
1492 do i = 1, num_nodes
1493 if(index(trim(nodes(i)%name), trim(prefix)) .ne. 1) cycle
1494 if(is_activation_op_type(trim(nodes(i)%op_type)))then
1495 actv_index = i
1496 return
1497 end if
1498 end do
1499
1500 end function find_activation_node_for_layer_id
1501 !###############################################################################
1502
1503
1504 !###############################################################################
1505 function is_activation_op_type(op_type) result(is_activation)
1506 !! Return true for ONNX activation nodes emitted by ATHENA export.
1507 implicit none
1508
1509 ! Arguments
1510 character(*), intent(in) :: op_type
1511 !! ONNX operation type string
1512
1513 logical :: is_activation
1514 !! Whether op_type matches an activation emitted by ATHENA export
1515
1516 select case(trim(op_type))
1517 case('Relu', 'LeakyRelu', 'Sigmoid', 'Softmax', 'Tanh', 'Selu', 'Swish')
1518 is_activation = .true.
1519 case default
1520 is_activation = .false.
1521 end select
1522
1523 end function is_activation_op_type
1524 !###############################################################################
1525
1526
1527 !###############################################################################
1528 8 subroutine sort_int_array(values)
1529 !! Sort an integer array in ascending order.
1530 implicit none
1531
1532 ! Arguments
1533 integer, allocatable, intent(inout) :: values(:)
1534 !! Integer array sorted in ascending order in-place
1535
1536 ! Local variables
1537 integer :: i, j, tmp
1538 !! Loop indices and swap temporary
1539
1540
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if(size(values) .le. 1) return
1541
1542
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 do i = 1, size(values) - 1
1543
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 do j = i + 1, size(values)
1544
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
10 if(values(j) .lt. values(i))then
1545 tmp = values(i)
1546 values(i) = values(j)
1547 values(j) = tmp
1548 end if
1549 end do
1550 end do
1551
1552 end subroutine sort_int_array
1553 !###############################################################################
1554
1555
1556 !###############################################################################
1557
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 function is_onnx_expanded_nop_graph(nodes, num_nodes) result(output)
1558 !! Return true when the parsed ONNX graph is a supported expanded-ONNX NOP
1559 !! decomposition that ATHENA can collapse back into native NOP layers.
1560 use athena__container_layer, only: &
1561 list_of_onnx_expanded_nop_layer_creators, &
1562 allocate_list_of_onnx_expanded_nop_layer_creators
1563 implicit none
1564
1565 ! Arguments
1566 type(onnx_node_type), intent(in) :: nodes(:)
1567 !! Parsed ONNX nodes
1568 integer, intent(in) :: num_nodes
1569 !! Number of valid node entries
1570
1571 logical :: output
1572 !! Whether the graph matches expanded-ONNX NOP patterns
1573
1574 ! Local variables
1575 6 character(32), allocatable :: layer_prefixes(:)
1576 !! Unique /layerN prefixes discovered in encounter order
1577 character(32) :: prefix
1578 !! Prefix extracted from the current node name
1579 integer :: i, j
1580 !! Loop indices
1581 logical :: recognised
1582 !! Whether at least one registered creator recognises the prefix
1583
1584
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if(.not.allocated(list_of_onnx_expanded_nop_layer_creators))then
1585 4 call allocate_list_of_onnx_expanded_nop_layer_creators()
1586 end if
1587
1588
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 allocate(layer_prefixes(0))
1589
1590
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 3 times.
34 do i = 1, num_nodes
1591
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
31 prefix = extract_onnx_expanded_layer_prefix(nodes(i)%name)
1592
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 28 times.
31 if(len_trim(prefix) .eq. 0)then
1593 3 output = .false.
1594 3 return
1595 end if
1596 31 call append_unique_onnx_expanded_prefix(prefix, layer_prefixes)
1597 end do
1598
1599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(size(layer_prefixes) .eq. 0)then
1600 output = .false.
1601 return
1602 end if
1603
1604
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 do i = 1, size(layer_prefixes)
1605 3 recognised = .false.
1606
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 do j = 1, size(list_of_onnx_expanded_nop_layer_creators)
1607
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
6 if(list_of_onnx_expanded_nop_layer_creators(j)%classify_ptr( &
1608
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
6 layer_prefixes(i), nodes, num_nodes))then
1609 3 recognised = .true.
1610 3 exit
1611 end if
1612 end do
1613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 if(.not.recognised)then
1614 output = .false.
1615 return
1616 end if
1617 end do
1618
1619 3 output = .true.
1620
1621
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 end function is_onnx_expanded_nop_graph
1622 !###############################################################################
1623
1624
1625 !###############################################################################
1626 3 subroutine build_network_from_json_onnx_expanded_nop( &
1627
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 network, nodes, num_nodes, inits, num_inits, verbose_)
1628 !! Reconstruct ATHENA NOP layers from an expanded-ONNX decomposed graph.
1629 !!
1630 !! Layer creation is delegated to the registered creators in
1631 !! list_of_onnx_expanded_nop_layer_creators, selected by their
1632 !! classify_ptr.
1633 use athena__base_layer, only: base_layer_type
1634 use athena__container_layer, only: &
1635 list_of_onnx_expanded_nop_layer_creators
1636 implicit none
1637
1638 ! Arguments
1639 type(network_type), intent(inout) :: network
1640 !! Network receiving the reconstructed layers
1641 type(onnx_node_type), intent(in) :: nodes(:)
1642 !! Parsed ONNX nodes
1643 integer, intent(in) :: num_nodes
1644 !! Number of valid node entries
1645 type(onnx_initialiser_type), intent(in) :: inits(:)
1646 !! Parsed ONNX initialisers
1647 integer, intent(in) :: num_inits
1648 !! Number of valid initialiser entries
1649 integer, intent(in) :: verbose_
1650 !! Effective verbosity level
1651
1652 ! Local variables
1653 3 character(32), allocatable :: layer_prefixes(:)
1654 !! Unique /layerN prefixes discovered in encounter order
1655 character(32) :: prefix
1656 !! Prefix extracted from the current node name
1657 integer :: i, j
1658 !! Loop indices
1659 9 class(base_layer_type), allocatable :: layer
1660 !! Constructed layer for each prefix
1661
1662
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 allocate(layer_prefixes(0))
1663
1664
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 3 times.
31 do i = 1, num_nodes
1665
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
28 prefix = extract_onnx_expanded_layer_prefix(nodes(i)%name)
1666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if(len_trim(prefix) .eq. 0) cycle
1667 31 call append_unique_onnx_expanded_prefix(prefix, layer_prefixes)
1668 end do
1669
1670
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 do i = 1, size(layer_prefixes)
1671
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
9 do j = 1, size(list_of_onnx_expanded_nop_layer_creators)
1672
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
6 if(list_of_onnx_expanded_nop_layer_creators(j)%classify_ptr( &
1673
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 6 times.
6 layer_prefixes(i), nodes, num_nodes))then
1674 layer = list_of_onnx_expanded_nop_layer_creators(j)%build_ptr( &
1675
13/30
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 3 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 3 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 3 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 3 times.
✓ Branch 30 taken 3 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 33 taken 3 times.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✓ Branch 38 taken 3 times.
✗ Branch 39 not taken.
✓ Branch 41 taken 3 times.
✗ Branch 42 not taken.
3 layer_prefixes(i), nodes, num_nodes, inits, num_inits)
1676 3 call network%add(layer)
1677 3 exit
1678 end if
1679 end do
1680 end do
1681
1682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(verbose_ .gt. 0)then
1683 write(*,*) 'Network built with ', network%num_layers, &
1684 ' expanded-ONNX NOP layers'
1685 end if
1686
1687
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 end subroutine build_network_from_json_onnx_expanded_nop
1688 !###############################################################################
1689
1690
1691 !###############################################################################
1692
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 function is_onnx_expanded_gnn_graph(nodes, num_nodes) result(output)
1693 !! Return true when the parsed ONNX graph contains expanded-ONNX GNN
1694 !! patterns that ATHENA can collapse back into native message passing
1695 !! layers.
1696 use athena__container_layer, only: &
1697 list_of_onnx_expanded_gnn_layer_creators, &
1698 allocate_list_of_onnx_expanded_gnn_layer_creators
1699 implicit none
1700
1701 ! Arguments
1702 type(onnx_node_type), intent(in) :: nodes(:)
1703 !! Parsed ONNX nodes
1704 integer, intent(in) :: num_nodes
1705 !! Number of valid node entries
1706
1707 logical :: output
1708 !! Whether the graph contains recognizable expanded-ONNX GNN patterns
1709
1710 ! Local variables
1711 3 integer, allocatable :: layer_ids(:)
1712 !! Unique layer ids from node names
1713 integer :: i, j, layer_id
1714 !! Loop indices and current layer id
1715 character(32) :: prefix
1716 !! Candidate GNN prefix
1717
1718
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(.not.allocated( &
1719 list_of_onnx_expanded_gnn_layer_creators))then
1720 3 call allocate_list_of_onnx_expanded_gnn_layer_creators()
1721 end if
1722
1723 3 output = .false.
1724
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 allocate(layer_ids(0))
1725
1726
2/2
✓ Branch 0 taken 201 times.
✓ Branch 1 taken 3 times.
204 do i = 1, num_nodes
1727 call parse_any_node_layer_id( &
1728
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 201 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 201 times.
201 nodes(i)%name, layer_id, j)
1729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 201 times.
201 if(j .le. 0) cycle
1730
10/14
✗ Branch 1 not taken.
✓ Branch 2 taken 201 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 201 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 201 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 201 times.
✓ Branch 13 taken 305 times.
✓ Branch 14 taken 9 times.
✓ Branch 15 taken 192 times.
✓ Branch 16 taken 113 times.
✓ Branch 17 taken 9 times.
✓ Branch 18 taken 192 times.
518 if(.not.any(layer_ids .eq. layer_id))then
1731
15/24
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 9 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 9 times.
✓ Branch 15 taken 13 times.
✓ Branch 16 taken 9 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 9 times.
✓ Branch 19 taken 22 times.
✓ Branch 20 taken 9 times.
✓ Branch 21 taken 9 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 9 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 9 times.
✓ Branch 27 taken 22 times.
✓ Branch 28 taken 9 times.
66 layer_ids = [layer_ids, layer_id]
1732 end if
1733 end do
1734
1735
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 do i = 1, size(layer_ids)
1736
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
7 write(prefix, '("node_",I0)') layer_ids(i)
1737
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 do j = 1, size( &
1738 8 list_of_onnx_expanded_gnn_layer_creators)
1739
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11 times.
13 if(list_of_onnx_expanded_gnn_layer_creators( &
1740 j)%classify_ptr( &
1741
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 13 times.
18 prefix, nodes, num_nodes))then
1742 2 output = .true.
1743 2 return
1744 end if
1745 end do
1746 end do
1747
1748
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 end function is_onnx_expanded_gnn_graph
1749 !###############################################################################
1750
1751
1752 !###############################################################################
1753 401 subroutine parse_any_node_layer_id( &
1754 node_name, layer_id, found)
1755 !! Parse layer id from node_X or node_X_* names.
1756 implicit none
1757
1758 ! Arguments
1759 character(*), intent(in) :: node_name
1760 !! Candidate ONNX node name
1761 integer, intent(out) :: layer_id
1762 !! Parsed layer id when present
1763 integer, intent(out) :: found
1764 !! Positive when parsing succeeded, zero otherwise
1765
1766 ! Local variables
1767 integer :: stat, upos
1768 !! Read status and underscore position
1769 character(128) :: rest
1770 !! Node name suffix after node_ prefix
1771
1772 401 layer_id = -1
1773 401 found = 0
1774
1775
2/4
✓ Branch 0 taken 401 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 401 times.
401 if(index(trim(node_name), 'node_') .ne. 1) return
1776
2/4
✓ Branch 1 taken 401 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 401 times.
✗ Branch 4 not taken.
401 rest = trim(node_name(6:))
1777
1778 ! Find end of the integer part
1779 401 upos = index(rest, '_')
1780
2/2
✓ Branch 0 taken 396 times.
✓ Branch 1 taken 5 times.
401 if(upos .gt. 0)then
1781
2/4
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 396 times.
396 read(rest(1:upos-1), *, iostat=stat) layer_id
1782 else
1783 5 read(rest, *, iostat=stat) layer_id
1784 end if
1785
1786
1/2
✓ Branch 0 taken 401 times.
✗ Branch 1 not taken.
401 if(stat .eq. 0 .and. layer_id .gt. 0) found = 1
1787
1788 401 end subroutine parse_any_node_layer_id
1789 !###############################################################################
1790
1791
1792 !###############################################################################
1793 2 subroutine build_network_from_json_onnx_expanded_gnn( &
1794
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 network, nodes, num_nodes, inits, num_inits, &
1795
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 inputs, num_inputs, verbose_)
1796 !! Reconstruct ATHENA GNN layers from an expanded-ONNX graph
1797 !! when metadata is absent.
1798 !!
1799 !! For each layer prefix, tries the registered GNN classifiers
1800 !! first. Unrecognised prefixes are handled as standard layers
1801 !! via the existing ONNX creator framework.
1802 use athena__base_layer, only: base_layer_type
1803 use athena__container_layer, only: &
1804 list_of_onnx_expanded_gnn_layer_creators, &
1805 list_of_onnx_layer_creators, &
1806 allocate_list_of_onnx_layer_creators
1807 implicit none
1808
1809 ! Arguments
1810 type(network_type), intent(inout) :: network
1811 !! Network receiving the reconstructed layers
1812 type(onnx_node_type), intent(in) :: nodes(:)
1813 !! Parsed ONNX nodes
1814 integer, intent(in) :: num_nodes
1815 !! Number of valid node entries
1816 type(onnx_initialiser_type), intent(in) :: inits(:)
1817 !! Parsed ONNX initialisers
1818 integer, intent(in) :: num_inits
1819 !! Number of valid initialiser entries
1820 type(onnx_tensor_type), intent(in) :: inputs(:)
1821 !! Parsed ONNX graph input tensors
1822 integer, intent(in) :: num_inputs
1823 !! Number of valid graph input entries
1824 integer, intent(in) :: verbose_
1825 !! Effective verbosity level
1826
1827 ! Local variables
1828 2 integer, allocatable :: ordered_ids(:)
1829 !! Sorted unique layer ids
1830 integer :: i, j, layer_id, node_index
1831 !! Loop indices and per-layer lookup results
1832 character(32) :: prefix
1833 !! Candidate layer prefix
1834 logical :: classified
1835 !! Whether a GNN classifier handled this prefix
1836 6 class(base_layer_type), allocatable :: layer
1837 !! Constructed layer
1838
1839
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(.not.allocated(list_of_onnx_layer_creators))then
1840 call allocate_list_of_onnx_layer_creators()
1841 end if
1842
1843
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 allocate(ordered_ids(0))
1844
1845 ! Collect layer ids from all node names
1846
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 2 times.
195 do i = 1, num_nodes
1847 call parse_any_node_layer_id( &
1848
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 193 times.
193 nodes(i)%name, layer_id, j)
1849
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
193 if(j .le. 0) cycle
1850
10/14
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 193 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 193 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 193 times.
✓ Branch 13 taken 285 times.
✓ Branch 14 taken 4 times.
✓ Branch 15 taken 189 times.
✓ Branch 16 taken 96 times.
✓ Branch 17 taken 4 times.
✓ Branch 18 taken 189 times.
484 if(.not.any(ordered_ids .eq. layer_id))then
1851
15/24
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 4 times.
✓ Branch 15 taken 3 times.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✓ Branch 19 taken 7 times.
✓ Branch 20 taken 4 times.
✓ Branch 21 taken 4 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 4 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 4 times.
✓ Branch 27 taken 7 times.
✓ Branch 28 taken 4 times.
21 ordered_ids = [ordered_ids, layer_id]
1852 end if
1853 end do
1854
1855 ! Also scan init names for layer ids
1856
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
9 do i = 1, num_inits
1857 call parse_any_node_layer_id( &
1858
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 inits(i)%name, layer_id, j)
1859
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if(j .le. 0) cycle
1860
8/14
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 7 times.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 7 times.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 7 times.
19 if(.not.any(ordered_ids .eq. layer_id))then
1861 ordered_ids = [ordered_ids, layer_id]
1862 end if
1863 end do
1864
1865 2 call sort_int_array(ordered_ids)
1866
1867
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 do i = 1, size(ordered_ids)
1868
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 layer_id = ordered_ids(i)
1869 4 write(prefix, '("node_",I0)') layer_id
1870
1871 ! Try GNN expanded classifiers
1872 4 classified = .false.
1873
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 do j = 1, size( &
1874 4 list_of_onnx_expanded_gnn_layer_creators)
1875
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 if(list_of_onnx_expanded_gnn_layer_creators( &
1876 j)%classify_ptr( &
1877
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
5 prefix, nodes, num_nodes))then
1878 layer = &
1879 list_of_onnx_expanded_gnn_layer_creators( &
1880 j)%build_ptr( &
1881 prefix, nodes, num_nodes, &
1882 inits, num_inits, &
1883
18/32
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 4 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 4 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 4 times.
✓ Branch 27 taken 2 times.
✓ Branch 28 taken 2 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 2 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 2 times.
✓ Branch 35 taken 4 times.
✗ Branch 36 not taken.
✓ Branch 38 taken 4 times.
✗ Branch 39 not taken.
6 inputs, num_inputs)
1884 4 call network%add(layer)
1885 4 classified = .true.
1886 4 exit
1887 end if
1888 end do
1889
1890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
6 if(.not.classified)then
1891 ! Try standard layer processing
1892 node_index = find_primary_node_for_layer_id( &
1893 nodes, num_nodes, layer_id)
1894 if(node_index .gt. 0)then
1895 call add_standard_layer_from_onnx( &
1896 network, layer_id, node_index, &
1897 nodes, num_nodes, &
1898 inits, num_inits, verbose_)
1899 end if
1900 end if
1901 end do
1902
1903
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(verbose_ .gt. 0)then
1904 write(*,*) 'Network built with ', &
1905 network%num_layers, &
1906 ' expanded-ONNX GNN layers'
1907 end if
1908
1909
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 end subroutine build_network_from_json_onnx_expanded_gnn
1910 !###############################################################################
1911
1912
1913 !###############################################################################
1914 subroutine append_unique_onnx_expanded_prefix(prefix, prefixes)
1915 !! Append a /layerN prefix to a list if it is not already present.
1916 implicit none
1917
1918 ! Arguments
1919 character(*), intent(in) :: prefix
1920 !! Prefix to append
1921 character(32), allocatable, intent(inout) :: prefixes(:)
1922 !! Prefix list updated in-place
1923
1924 ! Local variables
1925 integer :: i
1926 !! Loop index
1927
1928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if(len_trim(prefix) .eq. 0) return
1929
1930
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 6 times.
56 do i = 1, size(prefixes)
1931
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 50 times.
✓ Branch 8 taken 50 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 50 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 50 times.
✗ Branch 13 not taken.
56 if(trim(prefixes(i)) .eq. trim(prefix)) return
1932 end do
1933
1934
17/30
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 6 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 6 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 6 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 6 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 6 times.
✓ Branch 26 taken 6 times.
✓ Branch 27 taken 6 times.
✓ Branch 28 taken 6 times.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 6 times.
✓ Branch 34 taken 6 times.
✓ Branch 35 taken 6 times.
18 prefixes = [prefixes, prefix]
1935
1936
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 end subroutine append_unique_onnx_expanded_prefix
1937 !###############################################################################
1938
1939
1940 !###############################################################################
1941 59 function extract_onnx_expanded_layer_prefix(node_name) result(prefix)
1942 !! Extract the layerN prefix from an expanded-ONNX node name.
1943 implicit none
1944
1945 ! Arguments
1946 character(*), intent(in) :: node_name
1947 !! Node name such as /layer1/MatMul
1948 character(32) :: prefix
1949 !! Extracted layer prefix without leading slash
1950
1951 ! Local variables
1952 integer :: pos
1953 !! Position of the second slash in the node name
1954 character(128) :: trimmed_name
1955 !! Trimmed working copy of the node name
1956
1957 59 prefix = ''
1958
2/4
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
59 trimmed_name = trim(node_name)
1959
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 56 times.
59 if(index(trimmed_name, '/layer') .ne. 1) return
1960
1961 56 pos = index(trimmed_name(2:), '/')
1962
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if(pos .le. 0) return
1963
1964
3/6
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✓ Branch 5 taken 56 times.
✗ Branch 6 not taken.
56 prefix = trimmed_name(2:pos)
1965
1966 59 end function extract_onnx_expanded_layer_prefix
1967 !###############################################################################
1968
1969
1970 !###############################################################################
1971 6 subroutine build_network_from_json_standard( &
1972
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 network, nodes, num_nodes, inits, num_inits, inputs, num_inputs, &
1973 verbose_)
1974 !! Build a standard, non-GNN network from parsed JSON data.
1975 !!
1976 !! Synthetic value_info entries are created for layers whose output shape
1977 !! can be inferred from initialisers or simple attributes before calling
1978 !! build_from_onnx.
1979 implicit none
1980
1981 ! Arguments
1982 type(network_type), intent(inout) :: network
1983 !! Network to populate from parsed ONNX content
1984 type(onnx_node_type), intent(in) :: nodes(:)
1985 !! Parsed ONNX nodes
1986 integer, intent(in) :: num_nodes
1987 !! Number of valid entries in nodes
1988 type(onnx_initialiser_type), intent(in) :: inits(:)
1989 !! Parsed ONNX initialisers
1990 integer, intent(in) :: num_inits
1991 !! Number of valid entries in inits
1992 type(onnx_tensor_type), intent(in) :: inputs(:)
1993 !! Parsed graph input tensors
1994 integer, intent(in) :: num_inputs
1995 !! Number of valid entries in inputs
1996 integer, intent(in) :: verbose_
1997 !! Effective verbosity level
1998
1999 ! Local variables
2000 6 type(onnx_tensor_type), allocatable :: value_infos(:)
2001 !! Synthesised tensor value_info entries
2002 integer :: i, j, k, num_vi, ndims, n_kernel_dims
2003 !! Loop indices and temporary dimension counters
2004 character(128) :: out_name
2005 !! Current node output tensor name
2006 character(32) :: op_type_name
2007 !! Current node ONNX op type
2008
2009
5/8
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 3 times.
6 if(is_onnx_expanded_nop_graph(nodes, num_nodes))then
2010 call build_network_from_json_onnx_expanded_nop( &
2011
6/12
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 3 times.
3 network, nodes, num_nodes, inits, num_inits, verbose_)
2012 3 return
2013 end if
2014
2015
5/8
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
3 if(is_onnx_expanded_gnn_graph(nodes, num_nodes))then
2016 call build_network_from_json_onnx_expanded_gnn( &
2017 network, nodes, num_nodes, &
2018 inits, num_inits, &
2019
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 2 times.
2 inputs, num_inputs, verbose_)
2020 2 return
2021 end if
2022
2023
16/30
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 1 times.
✓ Branch 29 taken 8 times.
✓ Branch 30 taken 1 times.
✓ Branch 31 taken 8 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 8 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 8 times.
9 allocate(value_infos(num_nodes))
2024 1 num_vi = 0
2025
2026
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 node_loop: do i = 1, num_nodes
2027
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
8 if(.not.allocated(nodes(i)%outputs)) cycle
2028
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
8 if(nodes(i)%num_outputs .lt. 1) cycle
2029
2030
6/12
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
8 out_name = trim(nodes(i)%outputs(1))
2031
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
8 op_type_name = trim(adjustl(nodes(i)%op_type))
2032
2033
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 5 times.
16 do j = 1, nodes(i)%num_inputs
2034
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 8 times.
70 do k = 1, num_inits
2035
10/18
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 57 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 57 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 57 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 57 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 57 times.
✓ Branch 20 taken 57 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 57 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 54 times.
✓ Branch 25 taken 3 times.
57 if(trim(nodes(i)%inputs(j)) .ne. trim(inits(k)%name)) cycle
2036
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 if(.not.allocated(inits(k)%dims)) cycle
2037
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 if(size(inits(k)%dims) .lt. 2) cycle
2038
2039 3 num_vi = num_vi + 1
2040
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 value_infos(num_vi)%name = out_name
2041
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 value_infos(num_vi)%elem_type = 1
2042
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 ndims = size(inits(k)%dims)
2043
2044
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if(op_type_name .eq. 'Conv' .and. ndims .ge. 3)then
2045
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 1 times.
1 allocate(value_infos(num_vi)%dims(ndims))
2046
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
1 value_infos(num_vi)%dims(1) = 1
2047
8/16
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 1 times.
1 value_infos(num_vi)%dims(2) = inits(k)%dims(ndims)
2048
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
✓ Branch 18 taken 2 times.
✓ Branch 19 taken 1 times.
3 value_infos(num_vi)%dims(3:ndims) = 0
2049 else
2050
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
2 allocate(value_infos(num_vi)%dims(2))
2051
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
2 value_infos(num_vi)%dims(1) = 1
2052
8/16
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 2 times.
2 value_infos(num_vi)%dims(2) = inits(k)%dims(1)
2053 end if
2054 11 cycle node_loop
2055 end do
2056 end do
2057
2058
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if(index(op_type_name, 'Pool', back=.true.) .eq. &
2059 1 len_trim(op_type_name) - 3)then
2060 1 n_kernel_dims = 0
2061
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 if(allocated(nodes(i)%attributes))then
2062
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 do j = 1, size(nodes(i)%attributes)
2063
6/12
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
1 if(trim(adjustl(nodes(i)%attributes(j)%name)) .ne. &
2064 'kernel_shape') cycle
2065 block
2066 character(256) :: kval
2067 integer :: kpos, kstat, ktemp
2068
2069
6/12
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
1 kval = trim(adjustl(nodes(i)%attributes(j)%val))
2070 1 kpos = 1
2071
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 do while(kpos .le. len_trim(kval))
2072
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 do while(kpos .le. len_trim(kval) .and. &
2073
5/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
10 kval(kpos:kpos) .eq. ' ')
2074 3 kpos = kpos + 1
2075 end do
2076
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(kpos .gt. len_trim(kval)) exit
2077
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 read(kval(kpos:), *, iostat=kstat) ktemp
2078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(kstat .ne. 0) exit
2079 2 n_kernel_dims = n_kernel_dims + 1
2080
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 do while(kpos .le. len_trim(kval) .and. &
2081
5/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
8 kval(kpos:kpos) .ne. ' ')
2082 2 kpos = kpos + 1
2083 end do
2084 end do
2085 end block
2086 1 exit
2087 end do
2088 end if
2089
2090
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(n_kernel_dims .gt. 0)then
2091 1 num_vi = num_vi + 1
2092
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 value_infos(num_vi)%name = out_name
2093
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 value_infos(num_vi)%elem_type = 1
2094
10/20
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 1 times.
1 allocate(value_infos(num_vi)%dims(n_kernel_dims + 2))
2095
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 1 times.
5 value_infos(num_vi)%dims = 0
2096 end if
2097 end if
2098
2099 end do node_loop
2100
2101 call network%build_from_onnx( &
2102 nodes(1:num_nodes), inits(1:num_inits), inputs(1:num_inputs), &
2103
13/26
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 1 times.
1 value_infos(1:num_vi), verbose=verbose_)
2104
2105
8/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
15 end subroutine build_network_from_json_standard
2106 !###############################################################################
2107
2108
2109 !###############################################################################
2110 1672 subroutine extract_json_string(line, key, value)
2111 !! Extract a string value from a JSON key-value pair.
2112 implicit none
2113
2114 ! Arguments
2115 character(*), intent(in) :: line, key
2116 !! Source line and key token to find
2117 character(*), intent(out) :: value
2118 !! Extracted string value
2119
2120 ! Local variables
2121 integer :: pos, pos2, pos3
2122 !! Temporary indices used while slicing quoted text
2123
2124
1/2
✓ Branch 0 taken 1672 times.
✗ Branch 1 not taken.
1672 value = ''
2125
1/2
✓ Branch 1 taken 1672 times.
✗ Branch 2 not taken.
1672 pos = index(line, trim(key))
2126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1672 times.
1672 if(pos .eq. 0) return
2127
2128 1672 pos = pos + len_trim(key)
2129
2/4
✓ Branch 0 taken 1672 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1672 times.
1672 pos2 = index(line(pos:), '"')
2130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1672 times.
1672 if(pos2 .eq. 0) return
2131 1672 pos = pos + pos2
2132
2/4
✓ Branch 0 taken 1672 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1672 times.
1672 pos3 = index(line(pos:), '"')
2133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1672 times.
1672 if(pos3 .eq. 0) return
2134
6/12
✓ Branch 0 taken 1672 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1672 times.
✓ Branch 5 taken 1672 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1672 times.
✓ Branch 10 taken 1672 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1672 times.
✗ Branch 13 not taken.
1672 value = line(pos:pos+pos3-2)
2135
2136
1/2
✓ Branch 0 taken 1672 times.
✗ Branch 1 not taken.
1672 end subroutine extract_json_string
2137 !###############################################################################
2138
2139
2140 !###############################################################################
2141 81 subroutine extract_json_int(line, key, value)
2142 !! Extract an integer value from a JSON key-value pair.
2143 implicit none
2144
2145 ! Arguments
2146 character(*), intent(in) :: line, key
2147 !! Source line and key token to find
2148 integer, intent(out) :: value
2149 !! Extracted integer value
2150
2151 ! Local variables
2152 integer :: pos, pos2, stat
2153 !! Temporary indices and read status
2154 character(64) :: numstr
2155 !! Numeric substring buffer
2156
2157 81 value = 0
2158
1/2
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
81 pos = index(line, trim(key))
2159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 if(pos .eq. 0) return
2160
2161 81 pos = pos + len_trim(key)
2162
2/4
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
81 pos2 = index(line(pos:), ':')
2163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 if(pos2 .eq. 0) return
2164 81 pos = pos + pos2
2165
2166
3/6
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
✓ Branch 6 taken 81 times.
✗ Branch 7 not taken.
81 numstr = adjustl(line(pos:))
2167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 if(numstr(1:1) .eq. '"')then
2168 numstr = numstr(2:)
2169 pos2 = index(numstr, '"')
2170 if(pos2 .gt. 0) numstr = numstr(1:pos2-1)
2171 end if
2172 81 pos2 = index(numstr, ',')
2173
4/8
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 81 times.
✓ Branch 7 taken 81 times.
✗ Branch 8 not taken.
81 if(pos2 .gt. 0) numstr = numstr(1:pos2-1)
2174
2175 81 read(numstr, *, iostat=stat) value
2176
2177 81 end subroutine extract_json_int
2178 !###############################################################################
2179
2180
2181 !###############################################################################
2182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 772 times.
772 subroutine parse_json_string_array(line, key, values, n)
2183 !! Parse a JSON string array from one line.
2184 implicit none
2185
2186 ! Arguments
2187 character(*), intent(in) :: line, key
2188 !! Source line and array key token
2189 character(128), intent(inout) :: values(:)
2190 !! Destination array for parsed values
2191 integer, intent(inout) :: n
2192 !! Number of valid parsed values
2193
2194 ! Local variables
2195 integer :: pos, pos2, pos3
2196 !! Temporary indices while scanning quoted values
2197
2198
1/2
✓ Branch 1 taken 772 times.
✗ Branch 2 not taken.
772 pos = index(line, trim(key))
2199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 772 times.
772 if(pos .eq. 0) return
2200
2201 772 pos = pos + len_trim(key)
2202
2/4
✓ Branch 0 taken 772 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 772 times.
772 pos2 = index(line(pos:), '[')
2203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 772 times.
772 if(pos2 .eq. 0) return
2204 772 pos = pos + pos2
2205
2206 252 do
2207
2/4
✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1024 times.
1024 pos2 = index(line(pos:), '"')
2208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1024 times.
1024 if(pos2 .eq. 0) exit
2209 1024 pos = pos + pos2
2210
2/4
✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1024 times.
1024 pos3 = index(line(pos:), '"')
2211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1024 times.
1024 if(pos3 .eq. 0) exit
2212 1024 n = n + 1
2213
7/14
✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1024 times.
✓ Branch 5 taken 1024 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1024 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1024 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1024 times.
✓ Branch 16 taken 1024 times.
✗ Branch 17 not taken.
1024 values(n) = line(pos:pos+pos3-2)
2214 1024 pos = pos + pos3
2215
5/8
✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1024 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1024 times.
✓ Branch 7 taken 772 times.
✓ Branch 8 taken 252 times.
2048 if(index(line(pos:), ']') .gt. 0 .and. &
2216
1/2
✓ Branch 0 taken 1024 times.
✗ Branch 1 not taken.
1796 index(line(pos:), '"') .eq. 0) exit
2217
5/10
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 252 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 252 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 252 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 252 times.
756 if(index(line(pos:), ']') .gt. 0 .and. &
2218
2/4
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 252 times.
✗ Branch 4 not taken.
504 index(line(pos:), ']') .lt. index(line(pos:), '"')) exit
2219 end do
2220
2221
1/2
✓ Branch 0 taken 772 times.
✗ Branch 1 not taken.
772 end subroutine parse_json_string_array
2222 !###############################################################################
2223
2224
2225 !###############################################################################
2226 47 subroutine parse_json_int_array_from_strings(line, values)
2227 !! Parse a JSON array of string-encoded integers.
2228 implicit none
2229
2230 ! Arguments
2231 character(*), intent(in) :: line
2232 !! Source line containing a JSON array
2233 integer, allocatable, intent(inout) :: values(:)
2234 !! Parsed integer values
2235
2236 ! Local variables
2237 integer :: pos, pos2, pos3, stat, ival
2238 !! Temporary indices, read status and parsed integer value
2239 character(64) :: numstr
2240 !! Numeric token buffer
2241
2242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if(allocated(values)) deallocate(values)
2243
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 47 times.
47 allocate(values(0))
2244
2245 47 pos = index(line, '[')
2246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if(pos .eq. 0) return
2247 47 pos = pos + 1
2248
2249 45 do
2250
2/4
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
92 pos2 = index(line(pos:), '"')
2251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if(pos2 .eq. 0) exit
2252 92 pos = pos + pos2
2253
2/4
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
92 pos3 = index(line(pos:), '"')
2254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 if(pos3 .eq. 0) exit
2255
5/10
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
✓ Branch 5 taken 92 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 92 times.
✓ Branch 10 taken 92 times.
✗ Branch 11 not taken.
92 numstr = line(pos:pos+pos3-2)
2256 92 read(numstr, *, iostat=stat) ival
2257
16/26
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 92 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 92 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 92 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 92 times.
✓ Branch 17 taken 52 times.
✓ Branch 18 taken 92 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 92 times.
✓ Branch 21 taken 144 times.
✓ Branch 22 taken 92 times.
✓ Branch 23 taken 92 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 92 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 92 times.
✓ Branch 29 taken 144 times.
✓ Branch 30 taken 92 times.
432 if(stat .eq. 0) values = [values, ival]
2258 92 pos = pos + pos3
2259
7/12
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 92 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 92 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 92 times.
✓ Branch 11 taken 47 times.
✓ Branch 12 taken 45 times.
368 if(index(line(pos:), ']') .gt. 0 .and. &
2260
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
92 (index(line(pos:), '"') .eq. 0 .or. &
2261
2/4
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 92 times.
✗ Branch 4 not taken.
231 index(line(pos:), ']') .lt. index(line(pos:), '"'))) exit
2262 end do
2263
2264
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 end subroutine parse_json_int_array_from_strings
2265 !###############################################################################
2266
2267
2268 !###############################################################################
2269 233 subroutine parse_json_attribute(line, attrs, n_attrs)
2270 !! Parse one or more JSON attribute objects from a line.
2271 implicit none
2272
2273 ! Arguments
2274 character(*), intent(in) :: line
2275 !! Source line containing one or more JSON attribute objects
2276 type(onnx_attribute_type), allocatable, intent(inout) :: attrs(:)
2277 !! Destination list of parsed attributes
2278 integer, intent(inout) :: n_attrs
2279 !! Number of valid attributes in attrs
2280
2281 ! Local variables
2282 integer :: pos, brace_start, brace_end, depth, k
2283 !! Scan positions and brace depth state
2284
2285 233 pos = 1
2286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 499 times.
499 do while(pos .le. len_trim(line))
2287
2/4
✓ Branch 0 taken 499 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 499 times.
499 brace_start = index(line(pos:), '{')
2288
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 266 times.
499 if(brace_start .eq. 0) exit
2289 266 brace_start = pos + brace_start - 1
2290
2291 266 depth = 0
2292 266 brace_end = 0
2293
1/2
✓ Branch 0 taken 16935 times.
✗ Branch 1 not taken.
16935 do k = brace_start, len_trim(line)
2294
6/10
✓ Branch 0 taken 16935 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16935 times.
✓ Branch 5 taken 16935 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 16935 times.
✓ Branch 10 taken 364 times.
✓ Branch 11 taken 16571 times.
16935 if(line(k:k) .eq. '{') depth = depth + 1
2295
6/10
✓ Branch 0 taken 16935 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16935 times.
✓ Branch 5 taken 16935 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 16935 times.
✓ Branch 10 taken 364 times.
✓ Branch 11 taken 16571 times.
16935 if(line(k:k) .eq. '}') depth = depth - 1
2296
2/2
✓ Branch 0 taken 266 times.
✓ Branch 1 taken 16669 times.
16935 if(depth .eq. 0)then
2297 266 brace_end = k
2298 266 exit
2299 end if
2300 end do
2301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 if(brace_end .eq. 0) exit
2302
2303 call parse_single_json_attribute( &
2304
4/8
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
✓ Branch 5 taken 266 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 266 times.
266 line(brace_start:brace_end), attrs, n_attrs)
2305
2306 266 pos = brace_end + 1
2307 end do
2308
2309 233 end subroutine parse_json_attribute
2310 !###############################################################################
2311
2312
2313 !###############################################################################
2314 266 subroutine parse_single_json_attribute(line, attrs, n_attrs)
2315 !! Parse a single JSON attribute object.
2316 implicit none
2317
2318 ! Arguments
2319 character(*), intent(in) :: line
2320 !! Source line for one JSON attribute object
2321 type(onnx_attribute_type), allocatable, intent(inout) :: attrs(:)
2322 !! Destination list of parsed attributes
2323 integer, intent(inout) :: n_attrs
2324 !! Number of valid attributes in attrs
2325
2326 ! Local variables
2327 266 type(onnx_attribute_type) :: attr
2328 !! Parsed attribute record
2329 character(64) :: attr_type_str
2330 !! Raw attribute type token
2331 character(256) :: val_str
2332 !! Temporary attribute value buffer
2333
2334 266 attr%name = ''
2335
1/2
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
266 attr%type = ''
2336
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
266 allocate(character(0) :: attr%val)
2337
2338 266 call extract_json_string(line, '"name"', val_str)
2339
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
✓ Branch 3 taken 266 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 266 times.
✗ Branch 6 not taken.
266 attr%name = trim(val_str)
2340
2341 266 call extract_json_string(line, '"type"', attr_type_str)
2342
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
✓ Branch 4 taken 266 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 266 times.
✗ Branch 7 not taken.
266 attr%type = to_lower(trim(attr_type_str))
2343
2344 532 select case(trim(attr%type))
2345 case('int')
2346 128 call extract_json_string(line, '"i"', val_str)
2347
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 128 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 128 times.
✓ Branch 9 taken 128 times.
✗ Branch 10 not taken.
128 attr%val = trim(val_str)
2348 case('float')
2349 4 call extract_json_string(line, '"f"', val_str)
2350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(len_trim(val_str) .eq. 0)then
2351 block
2352 integer :: fp, fp2
2353
2354 fp = index(line, '"f"')
2355 if(fp .gt. 0)then
2356 fp = fp + 3
2357 fp = fp + index(line(fp:), ':')
2358 val_str = trim(adjustl(line(fp:)))
2359 fp2 = scan(val_str, ',}')
2360 if(fp2 .gt. 0) val_str = val_str(1:fp2-1)
2361 end if
2362 end block
2363 end if
2364
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
4 attr%val = trim(val_str)
2365 case('ints')
2366 22 block
2367 integer :: ip, ip2
2368 character(256) :: ints_str
2369
2370 22 ints_str = ''
2371 22 ip = index(line, '"ints"')
2372
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if(ip .gt. 0)then
2373 22 ip = ip + 6
2374
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 ip = ip + index(line(ip:), '[') - 1
2375
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 ip2 = index(line(ip:), ']')
2376
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 if(ip2 .gt. 0)then
2377
5/10
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 22 times.
✓ Branch 10 taken 22 times.
✗ Branch 11 not taken.
22 ints_str = line(ip+1:ip+ip2-2)
2378
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 22 times.
188 do ip2 = 1, len_trim(ints_str)
2379
10/18
✓ Branch 0 taken 166 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 166 times.
✓ Branch 5 taken 166 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 166 times.
✓ Branch 10 taken 166 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 166 times.
✓ Branch 15 taken 166 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 166 times.
✓ Branch 20 taken 107 times.
✓ Branch 21 taken 59 times.
166 if(ints_str(ip2:ip2) .eq. ',' .or. &
2380
4/8
✓ Branch 0 taken 107 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 107 times.
✓ Branch 5 taken 107 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 107 times.
129 ints_str(ip2:ip2) .eq. '"') ints_str(ip2:ip2) = ' '
2381 end do
2382 end if
2383 end if
2384
5/10
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 22 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 22 times.
✓ Branch 10 taken 22 times.
✗ Branch 11 not taken.
22 attr%val = trim(adjustl(ints_str))
2385 end block
2386 case('string')
2387 14 call extract_json_string(line, '"s"', val_str)
2388
5/10
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 14 times.
✗ Branch 10 not taken.
14 attr%val = trim(val_str)
2389 case default
2390
9/15
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 98 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 98 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 98 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
532 attr%val = ''
2391 end select
2392
2393 266 n_attrs = n_attrs + 1
2394
32/58
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 266 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 266 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 266 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 266 times.
✓ Branch 17 taken 57 times.
✓ Branch 18 taken 266 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 266 times.
✓ Branch 21 taken 323 times.
✓ Branch 22 taken 266 times.
✓ Branch 23 taken 323 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 323 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 323 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 323 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 266 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 266 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 266 times.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✓ Branch 41 taken 266 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 57 times.
✓ Branch 44 taken 266 times.
✓ Branch 45 taken 57 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 57 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 57 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 266 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 323 times.
✓ Branch 54 taken 266 times.
✓ Branch 55 taken 323 times.
✓ Branch 56 taken 266 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 323 times.
✗ Branch 59 not taken.
✓ Branch 60 taken 323 times.
✗ Branch 61 not taken.
✓ Branch 62 taken 323 times.
1881 attrs = [attrs, attr]
2395
2396
4/8
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 266 times.
✗ Branch 7 not taken.
532 end subroutine parse_single_json_attribute
2397 !###############################################################################
2398
2399 end submodule athena__onnx_read_submodule
2400