GCC Code Coverage Report


Directory: src/athena/
File: src/athena/athena_onnx_nop_utils.f90
Date: 2026-04-15 16:08:59
Exec Total Coverage
Lines: 139 161 86.3%
Functions: 0 0 -%
Branches: 328 651 50.4%

Line Branch Exec Source
1 module athena__onnx_nop_utils
2 !! Shared utility routines for NOP ONNX export/import.
3 use coreutils, only: real32, stop_program
4 use athena__base_layer, only: base_layer_type
5 use athena__misc_types, only: onnx_attribute_type, onnx_node_type, &
6 onnx_initialiser_type
7 use athena__onnx_utils, only: emit_node, col_to_row_major_2d, &
8 row_to_col_major_2d
9 use diffstruc, only: array_type
10 implicit none
11
12 private
13
14 public :: emit_nop_input_transpose
15 public :: emit_nop_output_tail
16 public :: emit_float_initialiser
17 public :: emit_matrix_initialiser
18 public :: emit_nop_metadata
19 public :: parse_nop_metadata
20 public :: extract_nop_prefix
21 public :: load_nop_param_from_inits
22 public :: find_initialiser_by_name
23 public :: infer_dynamic_lno_poles
24 public :: find_onnx_expanded_node_by_suffix
25 public :: find_node_initialiser_index
26 public :: detect_onnx_expanded_nop_activation
27 public :: load_onnx_expanded_matrix_param
28
29 contains
30
31 !###############################################################################
32 3 subroutine emit_nop_input_transpose(prefix, input_name, nodes, num_nodes, &
33 output_name)
34 !! Emit the common NOP input transpose.
35 implicit none
36
37 character(*), intent(in) :: prefix, input_name
38 type(onnx_node_type), intent(inout) :: nodes(:)
39 integer, intent(inout) :: num_nodes
40 character(*), intent(in) :: output_name
41
42 character(4096) :: perm_attr
43
44 perm_attr = ' "attribute": [{"name": "perm", "ints": ' // &
45 3 '["1", "0"], "type": "INTS"}]'
46
47 call emit_node('Transpose', '/' // trim(prefix) // '/Transpose', &
48 trim(output_name), trim(perm_attr), nodes, num_nodes, &
49
7/14
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 15 taken 3 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
3 in1=trim(input_name))
50
51
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 end subroutine emit_nop_input_transpose
52 !###############################################################################
53
54
55 !###############################################################################
56 3 subroutine emit_nop_output_tail(prefix, activation_name, is_last_layer, &
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 input_name, nodes, num_nodes, final_output)
58 !! Emit the common transpose and optional activation at the end of a NOP.
59 use coreutils, only: to_camel_case
60 implicit none
61
62 character(*), intent(in) :: prefix, activation_name, input_name
63 logical, intent(in) :: is_last_layer
64 type(onnx_node_type), intent(inout) :: nodes(:)
65 integer, intent(inout) :: num_nodes
66 character(128), intent(out) :: final_output
67
68 character(4096) :: perm_attr
69 character(128) :: transpose_output
70 character(128) :: activation_op, activation_node
71 character(4096) :: activation_attr
72
73 perm_attr = ' "attribute": [{"name": "perm", "ints": ' // &
74 3 '["1", "0"], "type": "INTS"}]'
75
76
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if(is_last_layer .and. trim(activation_name) .eq. 'none')then
77 transpose_output = 'output'
78 else
79 write(transpose_output, '("/",A,"/Transpose_1_output_0")') &
80
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 trim(prefix)
81 end if
82
83 call emit_node('Transpose', '/' // trim(prefix) // '/Transpose_1', &
84 trim(transpose_output), trim(perm_attr), nodes, num_nodes, &
85
7/14
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 15 taken 3 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
3 in1=trim(input_name))
86
87
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 if(trim(activation_name) .ne. 'none')then
88 activation_op = to_camel_case( &
89 trim(adjustl(activation_name)), &
90
2/4
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 capitalise_first_letter = .true.)
91 3 activation_attr = ''
92
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if(trim(activation_name) .eq. 'leaky_relu')then
93 activation_op = 'LeakyRelu'
94 activation_attr = ' "attribute": [{"name": "alpha", ' // &
95 '"f": 0.01, "type": "FLOAT"}]'
96 end if
97
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(is_last_layer)then
98 3 final_output = 'output'
99 else
100 write(final_output, '("/",A,"/",A,"_output_0")') &
101 trim(prefix), trim(activation_op)
102 end if
103
3/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
3 activation_node = '/' // trim(prefix) // '/' // trim(activation_op)
104 call emit_node(trim(activation_op), trim(activation_node), &
105 trim(final_output), trim(activation_attr), nodes, num_nodes, &
106
8/16
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
3 in1=trim(transpose_output))
107 else
108 final_output = transpose_output
109 end if
110
111
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 end subroutine emit_nop_output_tail
112 !###############################################################################
113
114
115 !###############################################################################
116
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 subroutine emit_float_initialiser(name, data, dims, inits, num_inits)
117 !! Emit a float32 initialiser with explicit dimensions.
118 implicit none
119
120 character(*), intent(in) :: name
121 real(real32), intent(in) :: data(:)
122 integer, intent(in) :: dims(:)
123 type(onnx_initialiser_type), intent(inout) :: inits(:)
124 integer, intent(inout) :: num_inits
125
126 13 num_inits = num_inits + 1
127
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
✓ Branch 7 taken 13 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 13 times.
✗ Branch 10 not taken.
13 inits(num_inits)%name = trim(name)
128
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 inits(num_inits)%data_type = 1
129
12/24
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 13 times.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 13 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 13 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 13 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 13 times.
13 allocate(inits(num_inits)%dims(size(dims)))
130
9/18
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 13 times.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 26 times.
✓ Branch 22 taken 13 times.
39 inits(num_inits)%dims = dims
131
12/24
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 13 times.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 13 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 13 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 13 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 13 times.
13 allocate(inits(num_inits)%data(size(data)))
132
9/18
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 13 times.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 1376 times.
✓ Branch 22 taken 13 times.
1389 inits(num_inits)%data = data
133
134
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 end subroutine emit_float_initialiser
135 !###############################################################################
136
137
138 !###############################################################################
139
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 subroutine emit_matrix_initialiser(name, data_col_major, rows, cols, inits, &
140 num_inits)
141 !! Emit a 2D float32 initialiser after converting to row-major order.
142 implicit none
143
144 character(*), intent(in) :: name
145 real(real32), intent(in) :: data_col_major(:)
146 integer, intent(in) :: rows, cols
147 type(onnx_initialiser_type), intent(inout) :: inits(:)
148 integer, intent(inout) :: num_inits
149
150 4 real(real32), allocatable :: row_major(:)
151
152
10/20
✗ 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 taken 4 times.
✗ Branch 10 not taken.
✗ 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.
4 allocate(row_major(size(data_col_major)))
153
4/8
✗ 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 11 not taken.
✓ Branch 12 taken 4 times.
4 call col_to_row_major_2d(data_col_major, row_major, rows, cols)
154 call emit_float_initialiser(name, row_major, [rows, cols], inits, &
155
5/8
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
12 num_inits)
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 deallocate(row_major)
157
158
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 end subroutine emit_matrix_initialiser
159 !###############################################################################
160
161
162 !###############################################################################
163 4 subroutine emit_nop_metadata(layer, prefix, metadata, num_meta)
164 !! Build the metadata entry required to reconstruct a NOP layer.
165 implicit none
166
167 class(base_layer_type), intent(in) :: layer
168 character(*), intent(in) :: prefix
169 character(4096), intent(inout) :: metadata(:)
170 integer, intent(inout) :: num_meta
171
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 type(onnx_attribute_type), allocatable :: attrs(:)
173 integer :: i
174 character(2048) :: value_str
175
176
11/34
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 19 times.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ 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 taken 19 times.
✓ Branch 28 taken 4 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 19 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 19 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 19 times.
46 attrs = layer%get_attributes()
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(.not.allocated(attrs)) return
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(size(attrs) .eq. 0) return
179
180
2/4
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
4 value_str = 'subtype=' // trim(adjustl(layer%name))
181
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 4 times.
23 do i = 1, size(attrs)
182 value_str = trim(value_str) // ';' // trim(attrs(i)%name) // '=' // &
183
8/16
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19 times.
✓ Branch 10 taken 19 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 19 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 19 times.
✓ Branch 22 taken 19 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 19 times.
✗ Branch 25 not taken.
23 trim(adjustl(attrs(i)%val))
184 end do
185
186 4 num_meta = num_meta + 1
187
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 write(metadata(num_meta), '(A)') &
188 ' {"key": "athena_nop_' // trim(prefix) // &
189
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
8 '", "value": "' // trim(value_str) // '"}'
190
191
8/14
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 19 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 19 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 19 times.
✗ Branch 13 not taken.
31 end subroutine emit_nop_metadata
192 !###############################################################################
193
194
195 !###############################################################################
196 4 subroutine parse_nop_metadata(meta_value, &
197 num_inputs, num_outputs, num_modes, use_bias, activation_name)
198 !! Parse common NOP hyperparameters from metadata value string.
199 implicit none
200
201 character(*), intent(in) :: meta_value
202 integer, intent(inout) :: num_inputs, num_outputs, num_modes
203 logical, intent(inout) :: use_bias
204 character(64), intent(inout) :: activation_name
205
206 integer :: k, pos, pos2, stat
207 character(256) :: token, key, val
208 logical :: logical_val
209
210 4 pos = 1
211
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 23 times.
27 do while(pos .le. len_trim(meta_value))
212
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 pos2 = index(meta_value(pos:), ';')
213
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if(pos2 .eq. 0)then
214
5/10
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
4 token = meta_value(pos:len_trim(meta_value))
215 4 pos = len_trim(meta_value) + 1
216 else
217
5/10
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
✓ Branch 5 taken 19 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 19 times.
✓ Branch 10 taken 19 times.
✗ Branch 11 not taken.
19 token = meta_value(pos:pos+pos2-2)
218 19 pos = pos + pos2
219 end if
220 23 k = index(token, '=')
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 if(k .eq. 0) cycle
222
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 23 times.
✗ Branch 10 not taken.
23 key = trim(adjustl(token(1:k-1)))
223
4/8
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
✓ Branch 7 taken 23 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 23 times.
✗ Branch 10 not taken.
23 val = trim(adjustl(token(k+1:)))
224 46 select case(trim(key))
225 case('num_inputs')
226 4 read(val, *) num_inputs
227 case('num_outputs')
228 4 read(val, *) num_outputs
229 case('num_modes', 'num_basis')
230 3 read(val, *) num_modes
231 case('use_bias')
232 4 read(val, *, iostat=stat) logical_val
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
8 if(stat .eq. 0)then
234 use_bias = logical_val
235 else
236 8 select case(trim(adjustl(val)))
237 case('1', 'T', 't', 'true', 'TRUE', 'True')
238 4 use_bias = .true.
239 case('0', 'F', 'f', 'false', 'FALSE', 'False')
240 use_bias = .false.
241 case default
242
2/5
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
8 call stop_program('parse_nop_metadata: invalid use_bias value')
243 end select
244 end if
245 case('activation')
246
9/12
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
46 activation_name = trim(val)
247 end select
248 end do
249
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 end subroutine parse_nop_metadata
251 !###############################################################################
252
253
254 !###############################################################################
255 4 function extract_nop_prefix(meta_key) result(prefix)
256 !! Extract the node prefix from an athena_nop_node_X metadata key.
257 implicit none
258
259 character(*), intent(in) :: meta_key
260 character(64) :: prefix
261
262 integer :: pos
263
264
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 prefix = trim(meta_key)
265 4 pos = index(prefix, 'athena_nop_')
266
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
4 if(pos .gt. 0) prefix = prefix(pos+11:)
267
268 4 end function extract_nop_prefix
269 !###############################################################################
270
271
272 !###############################################################################
273 14 subroutine load_nop_param_from_inits( &
274 14 param, prefix, suffix, inits, num_inits, dims)
275 !! Load a parameter from ONNX initialisers into a diffstruc array.
276 implicit none
277
278 type(array_type), intent(inout) :: param
279 character(*), intent(in) :: prefix, suffix
280 type(onnx_initialiser_type), intent(in) :: inits(:)
281 integer, intent(in) :: num_inits
282 integer, intent(in) :: dims(2)
283
284 integer :: k
285 character(128) :: target_name
286 14 real(real32), allocatable :: col_data(:)
287
288
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 write(target_name, '(A,A)') trim(prefix), suffix
289
290
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 do k = 1, num_inits
291
6/10
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 32 times.
✓ Branch 8 taken 32 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 32 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 18 times.
✓ Branch 13 taken 14 times.
32 if(trim(inits(k)%name) .ne. trim(target_name)) cycle
292
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
14 if(.not.allocated(inits(k)%data)) cycle
293
294
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 if(dims(2) .gt. 1)then
295
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 7 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 7 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 7 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 7 times.
7 allocate(col_data(size(inits(k)%data)))
296
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 call row_to_col_major_2d(inits(k)%data, col_data, dims(1), dims(2))
297
13/24
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 7 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 7 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 7 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 7 times.
✓ Branch 33 taken 1313 times.
✓ Branch 34 taken 7 times.
1320 param%val(:,1) = col_data
298
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
14 deallocate(col_data)
299 else
300
15/28
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 7 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 7 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 7 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 7 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 7 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 7 times.
✓ Branch 39 taken 68 times.
✓ Branch 40 taken 7 times.
75 param%val(:,1) = inits(k)%data
301 end if
302 14 return
303 end do
304
305
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
28 end subroutine load_nop_param_from_inits
306 !###############################################################################
307
308
309 !###############################################################################
310 40 integer function find_initialiser_by_name(name, inits, num_inits)
311 !! Return the index of a named initialiser, or zero when not found.
312 implicit none
313
314 character(*), intent(in) :: name
315 type(onnx_initialiser_type), intent(in) :: inits(:)
316 integer, intent(in) :: num_inits
317
318 integer :: i
319
320 40 find_initialiser_by_name = 0
321
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 9 times.
115 do i = 1, num_inits
322
6/10
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 106 times.
✓ Branch 8 taken 106 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 106 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 31 times.
✓ Branch 13 taken 75 times.
115 if(trim(inits(i)%name) .eq. trim(name))then
323 31 find_initialiser_by_name = i
324 31 return
325 end if
326 end do
327
328
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
49 end function find_initialiser_by_name
329 !###############################################################################
330
331
332 !###############################################################################
333 1 subroutine infer_dynamic_lno_poles(e_args_init, d_args_init, num_inputs, &
334
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 num_outputs, poles)
335 !! Reconstruct dynamic LNO poles from exported encoder/decoder arguments.
336 implicit none
337
338 type(onnx_initialiser_type), intent(in) :: e_args_init, d_args_init
339 integer, intent(in) :: num_inputs, num_outputs
340 real(real32), intent(out) :: poles(:)
341
342 integer :: k, idx, num_modes
343 real(real32) :: pi_value
344
345
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 num_modes = size(poles)
346
347
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(num_inputs .gt. 1 .and. allocated(e_args_init%data))then
348
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 do k = 1, num_modes
349 6 idx = (k - 1) * num_inputs + num_inputs
350
4/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 9 not taken.
✓ Branch 10 taken 6 times.
7 poles(k) = -e_args_init%data(idx)
351 end do
352 1 return
353 end if
354
355 if(num_outputs .gt. 1 .and. allocated(d_args_init%data))then
356 do k = 1, num_modes
357 idx = (num_outputs - 1) * num_modes + k
358 poles(k) = -d_args_init%data(idx)
359 end do
360 return
361 end if
362
363 pi_value = acos(-1.0_real32)
364 do k = 1, num_modes
365 poles(k) = real(k, real32) * pi_value
366 end do
367
368 end subroutine infer_dynamic_lno_poles
369 !###############################################################################
370
371 !###############################################################################
372 33 integer function find_onnx_expanded_node_by_suffix( &
373 33 nodes, num_nodes, prefix, suffix)
374 !! Return the node index matching one /layerN/suffix name, or zero.
375 implicit none
376
377 ! Arguments
378 type(onnx_node_type), intent(in) :: nodes(:)
379 !! Parsed ONNX nodes
380 integer, intent(in) :: num_nodes
381 !! Number of valid node entries
382 character(*), intent(in) :: prefix, suffix
383 !! Layer prefix and trailing node name token
384
385 ! Local variables
386 integer :: i
387 !! Loop index
388 character(128) :: target_name
389 !! Full node name to match
390
391
2/4
✓ Branch 3 taken 33 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 33 times.
✗ Branch 8 not taken.
33 write(target_name, '("/",A,"/",A)') trim(prefix), trim(suffix)
392 33 find_onnx_expanded_node_by_suffix = 0
393
394
2/2
✓ Branch 0 taken 191 times.
✓ Branch 1 taken 14 times.
205 do i = 1, num_nodes
395
6/10
✗ Branch 0 not taken.
✓ Branch 1 taken 191 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 191 times.
✓ Branch 8 taken 191 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 191 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 19 times.
✓ Branch 13 taken 172 times.
205 if(trim(nodes(i)%name) .eq. trim(target_name))then
396 19 find_onnx_expanded_node_by_suffix = i
397 19 return
398 end if
399 end do
400
401
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
47 end function find_onnx_expanded_node_by_suffix
402 !###############################################################################
403
404
405 !###############################################################################
406
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 integer function find_node_initialiser_index(node, inits, num_inits)
407 !! Return the first initialiser referenced by a node's inputs.
408 implicit none
409
410 ! Arguments
411 type(onnx_node_type), intent(in) :: node
412 !! Parsed ONNX node whose inputs may reference an initialiser
413 type(onnx_initialiser_type), intent(in) :: inits(:)
414 !! Parsed ONNX initialisers
415 integer, intent(in) :: num_inits
416 !! Number of valid initialiser entries
417
418 ! Local variables
419 integer :: i, init_idx
420 !! Loop index and candidate initialiser index
421
422 11 find_node_initialiser_index = 0
423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if(.not.allocated(node%inputs)) return
424
425
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 do i = 1, size(node%inputs)
426
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 14 times.
14 init_idx = find_initialiser_by_name(node%inputs(i), inits, num_inits)
427
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 if(init_idx .gt. 0)then
428 11 find_node_initialiser_index = init_idx
429 11 return
430 end if
431 end do
432
433 end function find_node_initialiser_index
434 !###############################################################################
435
436
437 !###############################################################################
438 3 function detect_onnx_expanded_nop_activation(prefix, nodes, num_nodes) &
439 result(name)
440 !! Reconstruct the activation name from the tail of an expanded-ONNX NOP
441 !! cluster.
442 use athena__onnx_utils, only: onnx_to_athena_activation
443 implicit none
444
445 ! Arguments
446 character(*), intent(in) :: prefix
447 !! Layer node prefix without leading slash
448 type(onnx_node_type), intent(in) :: nodes(:)
449 !! Parsed ONNX nodes
450 integer, intent(in) :: num_nodes
451 !! Number of valid node entries
452 character(64) :: name
453 !! Reconstructed ATHENA activation name
454 integer :: i
455 character(128) :: cluster_prefix
456
457
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 write(cluster_prefix, '("/",A,"/")') trim(prefix)
458 3 name = 'none'
459
460
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 do i = 1, num_nodes
461
5/10
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 28 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 28 times.
28 if(index(trim(nodes(i)%name), trim(cluster_prefix)) .ne. 1) cycle
462
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
56 select case(trim(nodes(i)%op_type))
463 case('Relu', 'LeakyRelu', 'Sigmoid', 'Softmax', 'Tanh', 'Selu', &
464 'Swish')
465
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
3 name = onnx_to_athena_activation(trim(nodes(i)%op_type))
466
3/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 25 times.
59 return
467 end select
468 end do
469
470
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 end function detect_onnx_expanded_nop_activation
471 !###############################################################################
472
473
474 !###############################################################################
475 4 subroutine load_onnx_expanded_matrix_param(param, init, rows, cols)
476 !! Copy a row-major ONNX matrix initialiser into a diffstruc parameter.
477 implicit none
478
479 ! Arguments
480 type(array_type), intent(inout) :: param
481 !! Destination diffstruc parameter tensor
482 type(onnx_initialiser_type), intent(in) :: init
483 !! Row-major ONNX initialiser data
484 integer, intent(in) :: rows, cols
485 !! Matrix shape
486
487 ! Local variables
488 4 real(real32), allocatable :: col_major(:)
489 !! Temporary column-major buffer for ATHENA internal storage
490
491
7/14
✓ 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 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 4 times.
4 allocate(col_major(rows * cols))
492 4 call row_to_col_major_2d(init%data, col_major, rows, cols)
493
13/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 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 4 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 4 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 4 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 4 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 4 times.
✓ Branch 33 taken 928 times.
✓ Branch 34 taken 4 times.
932 param%val(:,1) = col_major
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 deallocate(col_major)
495
496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 end subroutine load_onnx_expanded_matrix_param
497 !###############################################################################
498
499 end module athena__onnx_nop_utils
500