GCC Code Coverage Report


Directory: src/athena/
File: src/athena/athena_graph_nop_layer.f90
Date: 2026-04-15 16:08:59
Exec Total Coverage
Lines: 234 258 90.7%
Functions: 0 0 -%
Branches: 645 1387 46.5%

Line Branch Exec Source
1 module athena__graph_nop_layer
2 !! Module containing implementation of a Graph Neural Operator (GNO) layer
3 !!
4 !! This module implements a Graph Neural Operator layer that learns a
5 !! continuous kernel on graph edges. It combines a learnable kernel
6 !! network (small MLP) evaluated on relative coordinates with a linear
7 !! transform of the node features:
8 !!
9 !! \[ h_i^{(l+1)} = \sigma\!\left(
10 !! \mathbf{W}\,h_i^{(l)}
11 !! + \sum_{j \in \mathcal{N}(i)}
12 !! \kappa_\theta(x_i - x_j)\,h_j^{(l)}
13 !! \right) \]
14 !!
15 !! where:
16 !! - \(h_i^{(l)} \in \mathbb{R}^{F_{in}}\) is the node feature at layer l
17 !! - \(x_i \in \mathbb{R}^{d}\) is the node coordinate / attribute
18 !! - \(\kappa_\theta \colon \mathbb{R}^d \to \mathbb{R}^{F_{out} \times F_{in}}\)
19 !! is a learnable kernel MLP
20 !! - \(\mathbf{W} \in \mathbb{R}^{F_{out} \times F_{in}}\) is a learnable
21 !! linear (bypass) transform
22 !! - \(\sigma\) is the activation function
23 !!
24 !! The kernel MLP has one hidden layer:
25 !! \(\kappa_\theta(\Delta x) = V\,\text{relu}(U\,\Delta x + b_u) + b_v\)
26 !! where \(U \in \mathbb{R}^{H \times d}\),
27 !! \(V \in \mathbb{R}^{(F_{out} F_{in}) \times H}\),
28 !! and \(H\) is the hidden width of the kernel network.
29 !!
30 !! Input layout:
31 !! input(1,s) = node features [F_in x num_vertices]
32 !! input(2,s) = edge geometry / relative coords [d x num_edges]
33 !!
34 !! Number of learnable parameters:
35 !! Kernel MLP: \(H d + H + (F_{out} F_{in}) H + F_{out} F_{in}\)
36 !! Linear: \(F_{out} F_{in}\)
37 !! Bias: \(F_{out}\) (optional)
38 !!
39 !! This layer extends the message passing layer type and uses the diffstruc
40 !! autodiff framework to support physics-informed neural networks (PINNs).
41 !! The forward pass builds a computation graph through two differentiable
42 !! operations: `gno_kernel_eval` (kernel MLP evaluation on every edge) and
43 !! `gno_aggregate` (neighbour message aggregation), followed by the standard
44 !! `matmul`, `add_bias`, and activation operations.
45 use coreutils, only: real32, stop_program
46 use graphstruc, only: graph_type
47 use athena__base_layer, only: base_layer_type
48 use athena__msgpass_layer, only: msgpass_layer_type
49 use athena__misc_types, only: base_actv_type, base_init_type
50 use diffstruc, only: array_type, matmul, operator(+)
51 use athena__diffstruc_extd, only: add_bias, gno_kernel_eval, gno_aggregate
52 implicit none
53
54
55 private
56
57 public :: graph_nop_layer_type
58 public :: read_graph_nop_layer
59
60
61 type, extends(msgpass_layer_type) :: graph_nop_layer_type
62 !! Type for a Graph Neural Operator layer
63 integer :: coord_dim = 0
64 !! Dimensionality of edge geometric features (d)
65 integer :: kernel_hidden = 0
66 !! Hidden width of the kernel MLP (H)
67 contains
68 procedure, pass(this) :: get_num_params => get_num_params_gno
69 procedure, pass(this) :: set_hyperparams => set_hyperparams_gno
70 procedure, pass(this) :: init => init_gno
71 procedure, pass(this) :: print_to_unit => print_to_unit_gno
72 procedure, pass(this) :: read => read_gno
73
74 procedure, pass(this) :: update_message => update_message_gno
75 procedure, pass(this) :: update_readout => update_readout_gno
76 end type graph_nop_layer_type
77
78 interface graph_nop_layer_type
79 module function layer_setup( &
80 num_outputs, coord_dim, kernel_hidden, &
81 num_inputs, use_bias, &
82 activation, &
83 kernel_initialiser, bias_initialiser, &
84 verbose &
85 ) result(layer)
86 integer, intent(in) :: num_outputs
87 !! Number of output node features
88 integer, intent(in) :: coord_dim
89 !! Dimensionality of edge geometric features
90 integer, optional, intent(in) :: kernel_hidden
91 !! Hidden width of kernel MLP (default: num_outputs)
92 integer, optional, intent(in) :: num_inputs
93 !! Number of input node features (deferred if absent)
94 logical, optional, intent(in) :: use_bias
95 !! Whether to use bias (default: .true.)
96 class(*), optional, intent(in) :: activation
97 !! Activation function
98 class(*), optional, intent(in) :: kernel_initialiser, bias_initialiser
99 !! Parameter initialisers
100 integer, optional, intent(in) :: verbose
101 !! Verbosity level
102 type(graph_nop_layer_type) :: layer
103 end function layer_setup
104 end interface graph_nop_layer_type
105
106
107 contains
108
109
110 !###############################################################################
111 13 pure function get_num_params_gno(this) result(num_params)
112 !! Get the number of learnable parameters
113 !!
114 !! Parameters:
115 !! params(1): packed kernel MLP [H*d + H + F*H + F, 1]
116 !! where F = F_out * F_in
117 !! Layout: U [H*d] | b_u [H] | V [F*H] | b_v [F]
118 !! params(2): W - linear bypass weights [F_out * F_in, 1]
119 !! params(3): b - output bias [F_out, 1] (optional)
120 implicit none
121
122 ! Arguments
123 class(graph_nop_layer_type), intent(in) :: this
124 !! Layer instance
125 integer :: num_params
126 !! Total number of learnable parameters
127
128 ! Local variables
129 integer :: F_in, F_out, d, H, F
130 !! Input/output feature counts, coordinate size, hidden width and flattened kernel width
131
132
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 F_in = this%num_vertex_features(0)
133
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 F_out = this%num_vertex_features(1)
134 13 d = this%coord_dim
135 13 H = this%kernel_hidden
136 13 F = F_out * F_in
137
138 num_params = &
139 H * d + H + F * H + F + & ! kernel MLP (U, b_u, V, b_v)
140 13 F_out * F_in ! W (linear bypass)
141
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if(this%use_bias) num_params = num_params + F_out ! b
142
143 13 end function get_num_params_gno
144 !###############################################################################
145
146
147 !###############################################################################
148 13 module function layer_setup( &
149 num_outputs, coord_dim, kernel_hidden, &
150 num_inputs, use_bias, &
151 activation, &
152 kernel_initialiser, bias_initialiser, &
153 verbose &
154 13 ) result(layer)
155 use athena__activation, only: activation_setup
156 use athena__initialiser, only: initialiser_setup
157 implicit none
158
159 ! Arguments
160 integer, intent(in) :: num_outputs
161 !! Number of output node features
162 integer, intent(in) :: coord_dim
163 !! Dimension of edge coordinate features
164 integer, optional, intent(in) :: kernel_hidden
165 !! Hidden width of the kernel MLP
166 integer, optional, intent(in) :: num_inputs
167 !! Number of input node features when known at construction time
168 logical, optional, intent(in) :: use_bias
169 !! Whether to allocate an output bias
170 class(*), optional, intent(in) :: activation
171 !! Activation function specification
172 class(*), optional, intent(in) :: kernel_initialiser, bias_initialiser
173 !! Kernel and bias initialiser specifications
174 integer, optional, intent(in) :: verbose
175 !! Verbosity level
176
177 type(graph_nop_layer_type) :: layer
178 !! Constructed graph neural operator layer
179
180 ! Local variables
181 integer :: verbose_ = 0
182 !! Effective verbosity level
183 logical :: use_bias_ = .true.
184 !! Effective bias flag
185 integer :: kernel_hidden_
186 !! Effective kernel hidden width
187 39 class(base_actv_type), allocatable :: activation_
188 !! Materialised activation object
189 39 class(base_init_type), allocatable :: kernel_initialiser_, bias_initialiser_
190 !! Materialised kernel and bias initialisers
191
192 if(present(verbose)) verbose_ = verbose
193
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13 if(present(use_bias)) use_bias_ = use_bias
194 13 kernel_hidden_ = num_outputs
195
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
13 if(present(kernel_hidden)) kernel_hidden_ = kernel_hidden
196
197
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
13 if(present(activation))then
198
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 8 times.
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
8 activation_ = activation_setup(activation)
199 else
200
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 5 times.
✓ Branch 17 taken 5 times.
✗ Branch 18 not taken.
5 activation_ = activation_setup("none")
201 end if
202
203
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(present(kernel_initialiser))then
204 kernel_initialiser_ = initialiser_setup(kernel_initialiser)
205 end if
206
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(present(bias_initialiser))then
207 bias_initialiser_ = initialiser_setup(bias_initialiser)
208 end if
209
210 call layer%set_hyperparams( &
211 num_outputs = num_outputs, &
212 coord_dim = coord_dim, &
213 kernel_hidden = kernel_hidden_, &
214 use_bias = use_bias_, &
215 activation = activation_, &
216 kernel_initialiser = kernel_initialiser_, &
217 bias_initialiser = bias_initialiser_, &
218 verbose = verbose_ &
219 13 )
220
221
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 11 times.
35 if(present(num_inputs)) call layer%init(input_shape=[num_inputs, 0])
222
223
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 13 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 12 taken 13 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 13 times.
26 end function layer_setup
224 !###############################################################################
225
226
227 !###############################################################################
228 14 subroutine set_hyperparams_gno( &
229 this, num_outputs, coord_dim, kernel_hidden, &
230 use_bias, &
231 activation, &
232 kernel_initialiser, bias_initialiser, &
233 verbose &
234 )
235 use athena__activation, only: activation_setup
236 use athena__initialiser, only: get_default_initialiser, initialiser_setup
237 implicit none
238
239 ! Arguments
240 class(graph_nop_layer_type), intent(inout) :: this
241 !! Layer instance to configure
242 integer, intent(in) :: num_outputs
243 !! Number of output node features
244 integer, intent(in) :: coord_dim
245 !! Dimension of edge coordinate features
246 integer, intent(in) :: kernel_hidden
247 !! Hidden width of the kernel MLP
248 logical, intent(in) :: use_bias
249 !! Whether to use a bias term
250 class(base_actv_type), allocatable, intent(in) :: activation
251 !! Activation function object
252 class(base_init_type), allocatable, intent(in) :: &
253 kernel_initialiser, bias_initialiser
254 !! Kernel and bias initialiser objects
255 integer, optional, intent(in) :: verbose
256 !! Verbosity level
257
258 ! Local variables
259 character(len=256) :: buffer
260 !! Buffer for default initialiser lookup
261
262
5/8
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
14 this%name = "graph_nop"
263 14 this%type = "gnop"
264 14 this%input_rank = 2
265 14 this%output_rank = 2
266 14 this%use_graph_input = .true.
267 14 this%use_graph_output = .true.
268 14 this%use_bias = use_bias
269 14 this%num_outputs = num_outputs
270 14 this%coord_dim = coord_dim
271 14 this%kernel_hidden = kernel_hidden
272 14 this%num_time_steps = 1
273
274
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
14 if(allocated(this%activation)) deallocate(this%activation)
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(.not.allocated(activation))then
276 this%activation = activation_setup("none")
277 else
278
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
14 allocate(this%activation, source=activation)
279 end if
280
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
14 if(allocated(this%kernel_init)) deallocate(this%kernel_init)
281
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if(.not.allocated(kernel_initialiser))then
282
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 buffer = get_default_initialiser(this%activation%name)
283
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 13 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 13 times.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
13 this%kernel_init = initialiser_setup(buffer)
284 else
285
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 allocate(this%kernel_init, source=kernel_initialiser)
286 end if
287
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
14 if(allocated(this%bias_init)) deallocate(this%bias_init)
288
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if(.not.allocated(bias_initialiser))then
289 buffer = get_default_initialiser( &
290 this%activation%name, &
291 is_bias=.true. &
292
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 )
293
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 13 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 13 times.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
13 this%bias_init = initialiser_setup(buffer)
294 else
295
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 allocate(this%bias_init, source=bias_initialiser)
296 end if
297
298
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if(present(verbose))then
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(abs(verbose).gt.0)then
300 write(*,'("GRAPH_NOP activation: ",A)') &
301 trim(this%activation%name)
302 end if
303 end if
304
305 14 end subroutine set_hyperparams_gno
306 !###############################################################################
307
308
309 !###############################################################################
310
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 subroutine init_gno(this, input_shape, verbose)
311 !! Initialise the Graph Neural Operator layer
312 !!
313 !! input_shape(1) = num_inputs (F_in)
314 !! input_shape(2) = num_vertices (set to 0 if variable)
315 implicit none
316
317 ! Arguments
318 class(graph_nop_layer_type), intent(inout) :: this
319 !! Layer instance to initialise
320 integer, dimension(:), intent(in) :: input_shape
321 !! Input feature/vertex shape
322 integer, optional, intent(in) :: verbose
323 !! Verbosity level
324
325 ! Local variables
326 integer :: num_inputs, H, F_out, F_in, d, F
327 !! Effective input count and kernel dimensions
328 integer :: kernel_size, off_U, off_bu, off_V, off_bv
329 !! Packed-kernel size and section offsets
330 integer :: verbose_ = 0
331 !! Effective verbosity level
332
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(present(verbose)) verbose_ = verbose
334
335 !---------------------------------------------------------------------------
336 ! Set shapes
337 !---------------------------------------------------------------------------
338
4/8
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 13 times.
13 if(.not.allocated(this%input_shape)) call this%set_shape(input_shape)
339
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 F_in = input_shape(1)
341 13 F_out = this%num_outputs
342 13 d = this%coord_dim
343 13 H = this%kernel_hidden
344 13 F = F_out * F_in
345
346 !---------------------------------------------------------------------------
347 ! Set msgpass fields
348 !---------------------------------------------------------------------------
349
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(allocated(this%num_vertex_features)) deallocate(this%num_vertex_features)
350
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.
13 allocate(this%num_vertex_features(0:1))
351
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%num_vertex_features(0) = F_in
352
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%num_vertex_features(1) = F_out
353
354
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(allocated(this%num_edge_features)) deallocate(this%num_edge_features)
355
9/16
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✓ Branch 21 taken 26 times.
✓ Branch 22 taken 13 times.
39 allocate(this%num_edge_features(0:1), source=0)
356
357 13 kernel_size = H * d + H + F * H + F
358
359
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(allocated(this%num_params_msg)) deallocate(this%num_params_msg)
360
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.
13 allocate(this%num_params_msg(1))
361
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%num_params_msg(1) = kernel_size + F_out * F_in
362
6/10
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 11 times.
13 if(this%use_bias) this%num_params_msg(1) = this%num_params_msg(1) + F_out
363 13 this%num_params_readout = 0
364
365
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
✓ Branch 7 taken 13 times.
39 this%output_shape = [this%num_outputs, 0]
366 13 this%num_params = this%get_num_params()
367
368 !---------------------------------------------------------------------------
369 ! Allocate learnable parameters
370 !
371 ! params(1): packed kernel MLP [kernel_size, 1]
372 ! Layout: U [H*d] | b_u [H] | V [F*H] | b_v [F]
373 ! params(2): W [F_out*F_in, 1] - linear bypass weights
374 ! params(3): b [F_out, 1] - output bias (optional)
375 !---------------------------------------------------------------------------
376
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(allocated(this%weight_shape)) deallocate(this%weight_shape)
377
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13 if(allocated(this%params)) deallocate(this%params)
378
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if(this%use_bias)then
379
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
11 if(allocated(this%bias_shape)) deallocate(this%bias_shape)
380
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 11 times.
22 this%bias_shape = [ F_out ]
381
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
11 allocate(this%weight_shape(2, 3))
382
9/16
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 11 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 11 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 11 times.
✓ Branch 21 taken 22 times.
✓ Branch 22 taken 11 times.
33 this%weight_shape(:,3) = [ F_out, 1 ]
383
16/30
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 11 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 11 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 11 times.
✓ Branch 21 taken 33 times.
✓ Branch 22 taken 11 times.
✓ Branch 23 taken 33 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 33 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 33 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 33 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 33 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 33 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 33 times.
44 allocate(this%params(3))
384 else
385
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(this%weight_shape(2, 2))
386
16/30
✗ 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 taken 4 times.
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 4 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 4 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 4 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 4 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 4 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 4 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 4 times.
6 allocate(this%params(2))
387 end if
388
9/16
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✓ Branch 21 taken 26 times.
✓ Branch 22 taken 13 times.
39 this%weight_shape(:,1) = [ kernel_size, 1 ]
389
9/16
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✓ Branch 21 taken 26 times.
✓ Branch 22 taken 13 times.
39 this%weight_shape(:,2) = [ F_out, F_in ]
390
391 ! params(1): packed kernel MLP params
392
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✓ Branch 6 taken 26 times.
✓ Branch 7 taken 13 times.
39 call this%params(1)%allocate([kernel_size, 1])
393
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 call this%params(1)%set_requires_grad(.true.)
394
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(1)%fix_pointer = .true.
395
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(1)%is_sample_dependent = .false.
396
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(1)%is_temporary = .false.
397
398 ! params(2): W [F_out x F_in]
399
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✓ Branch 6 taken 39 times.
✓ Branch 7 taken 13 times.
52 call this%params(2)%allocate([F_out, F_in, 1])
400
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 call this%params(2)%set_requires_grad(.true.)
401
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(2)%fix_pointer = .true.
402
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(2)%is_sample_dependent = .false.
403
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
13 this%params(2)%is_temporary = .false.
404
405
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if(this%use_bias)then
406 ! params(3): b [F_out]
407
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 6 taken 22 times.
✓ Branch 7 taken 11 times.
33 call this%params(3)%allocate([F_out, 1])
408
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 call this%params(3)%set_requires_grad(.true.)
409
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 this%params(3)%fix_pointer = .true.
410
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 this%params(3)%is_sample_dependent = .false.
411
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
11 this%params(3)%is_temporary = .false.
412 end if
413
414
415 !---------------------------------------------------------------------------
416 ! Initialise learnable parameters
417 !---------------------------------------------------------------------------
418 13 off_U = 0
419 13 off_bu = H * d
420 13 off_V = off_bu + H
421 13 off_bv = off_V + F * H
422
423 ! U [H x d] — kernel first-layer weights
424 call this%kernel_init%initialise( &
425 130 this%params(1)%val(off_U+1:off_bu, 1), &
426 fan_in = d, fan_out = H, &
427 spacing = [ H ] &
428
12/22
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 13 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 13 times.
✓ Branch 30 taken 13 times.
✓ Branch 31 taken 13 times.
26 )
429 ! b_u [H] — kernel first-layer bias
430 call this%bias_init%initialise( &
431 130 this%params(1)%val(off_bu+1:off_V, 1), &
432 fan_in = d, fan_out = H &
433
10/20
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 13 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 13 times.
13 )
434 ! V [F x H] — kernel second-layer weights
435 call this%kernel_init%initialise( &
436 130 this%params(1)%val(off_V+1:off_bv, 1), &
437 fan_in = H, fan_out = F, &
438 spacing = [ F ] &
439
12/22
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 13 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 13 times.
✓ Branch 30 taken 13 times.
✓ Branch 31 taken 13 times.
26 )
440 ! b_v [F] — kernel second-layer bias
441 call this%bias_init%initialise( &
442 130 this%params(1)%val(off_bv+1:, 1), &
443 fan_in = H, fan_out = F &
444
10/20
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 13 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 13 times.
13 )
445 ! W [F_out x F_in] — linear bypass
446 13 num_inputs = F_in
447
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if(this%use_bias) num_inputs = F_in + 1
448 call this%kernel_init%initialise( &
449 130 this%params(2)%val(:,1), &
450 fan_in = num_inputs, fan_out = F_out, &
451 spacing = [ F_out ] &
452
12/22
✗ 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 not taken.
✓ Branch 16 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 13 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 13 times.
✓ Branch 30 taken 13 times.
✓ Branch 31 taken 13 times.
26 )
453
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if(this%use_bias)then
454 call this%bias_init%initialise( &
455 110 this%params(3)%val(:,1), &
456 fan_in = num_inputs, fan_out = F_out &
457
10/20
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 11 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 11 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 11 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 11 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 11 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 11 times.
11 )
458 end if
459
460
461 !---------------------------------------------------------------------------
462 ! Allocate output arrays
463 !---------------------------------------------------------------------------
464
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
13 if(allocated(this%output)) deallocate(this%output)
465
466 13 end subroutine init_gno
467 !###############################################################################
468
469
470 !###############################################################################
471 1 subroutine print_to_unit_gno(this, unit)
472 !! Print graph neural operator settings and parameters to a unit
473 use coreutils, only: to_upper
474 implicit none
475
476 ! Arguments
477 class(graph_nop_layer_type), intent(in) :: this
478 !! Layer instance to print
479 integer, intent(in) :: unit
480 !! Output unit number
481
482 ! Local variables
483 integer :: p
484 !! Parameter block index
485
486
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 write(unit,'(3X,"NUM_INPUTS = ",I0)') this%num_vertex_features(0)
487 1 write(unit,'(3X,"NUM_OUTPUTS = ",I0)') this%num_outputs
488 1 write(unit,'(3X,"COORD_DIM = ",I0)') this%coord_dim
489 1 write(unit,'(3X,"KERNEL_HIDDEN = ",I0)') this%kernel_hidden
490 1 write(unit,'(3X,"USE_BIAS = ",L1)') this%use_bias
491
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(this%activation%name .ne. 'none')then
492 1 call this%activation%print_to_unit(unit)
493 end if
494
495 1 write(unit,'("WEIGHTS")')
496
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 do p = 1, size(this%params)
497
10/18
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 3 times.
✓ Branch 25 taken 102 times.
✓ Branch 26 taken 3 times.
106 write(unit,'(5(E16.8E2))') this%params(p)%val(:,1)
498 end do
499 1 write(unit,'("END WEIGHTS")')
500
501 1 end subroutine print_to_unit_gno
502 !###############################################################################
503
504
505 !###############################################################################
506 1 subroutine read_gno(this, unit, verbose)
507 use athena__tools_infile, only: assign_val, move
508 use coreutils, only: to_lower, to_upper, icount
509 use athena__activation, only: read_activation
510 use athena__initialiser, only: initialiser_setup
511 implicit none
512
513 ! Arguments
514 class(graph_nop_layer_type), intent(inout) :: this
515 !! Layer instance to populate from file data
516 integer, intent(in) :: unit
517 !! Input unit number
518 integer, optional, intent(in) :: verbose
519 !! Verbosity level
520
521 ! Local variables
522 integer :: stat, verbose_ = 0
523 !! I/O status and effective verbosity level
524 integer :: j, k, c, itmp1, iline
525 !! Loop counters and parser scratch integers
526 integer :: num_inputs, num_outputs, coord_dim, kernel_hidden
527 !! Parsed layer dimensions
528 logical :: use_bias = .true.
529 !! Parsed bias flag
530 character(14) :: kernel_initialiser_name='', bias_initialiser_name=''
531 !! Parsed initialiser names
532 3 class(base_actv_type), allocatable :: activation
533 !! Parsed activation object
534 5 class(base_init_type), allocatable :: kernel_initialiser, bias_initialiser
535 !! Parsed initialiser objects
536 character(256) :: buffer, tag, err_msg
537 !! Input buffer, parsed tag and formatted error message
538 1 real(real32), allocatable, dimension(:) :: data_list
539 !! Temporary storage for flattened parameter blocks
540 integer :: param_line, final_line, num_vals, p
541 !! Weights-section line markers, current block size and parameter index
542
543
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(present(verbose)) verbose_ = verbose
544
545 1 iline = 0
546 1 param_line = 0
547 1 final_line = 0
548 30 tag_loop: do
549 31 read(unit,'(A)',iostat=stat) buffer
550
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if(stat.ne.0)then
551 write(err_msg, &
552 '("file encountered error (EoF?) before END ",A)') &
553 to_upper(this%name)
554 call stop_program(err_msg)
555 return
556 end if
557
2/4
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
31 if(trim(adjustl(buffer)).eq."") cycle tag_loop
558
559
4/6
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 30 times.
62 if(trim(adjustl(buffer)).eq."END "//to_upper(trim(this%name)))then
560 1 final_line = iline
561 1 backspace(unit)
562 31 exit tag_loop
563 end if
564 30 iline = iline + 1
565
566
2/4
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
30 tag = trim(adjustl(buffer))
567
6/10
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 11 not taken.
30 if(scan(buffer,"=").ne.0) tag = trim(tag(:scan(tag,"=")-1))
568
569 60 select case(trim(tag))
570 case("NUM_INPUTS")
571 2 call assign_val(buffer, num_inputs, itmp1)
572 case("NUM_OUTPUTS")
573 2 call assign_val(buffer, num_outputs, itmp1)
574 case("COORD_DIM")
575 2 call assign_val(buffer, coord_dim, itmp1)
576 case("KERNEL_HIDDEN")
577 2 call assign_val(buffer, kernel_hidden, itmp1)
578 case("USE_BIAS")
579 2 call assign_val(buffer, use_bias, itmp1)
580 case("ACTIVATION")
581 1 iline = iline - 1
582 1 backspace(unit)
583
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
1 activation = read_activation(unit, iline)
584 case("KERNEL_INITIALISER", "KERNEL_INIT", "KERNEL_INITIALIZER")
585 call assign_val(buffer, kernel_initialiser_name, itmp1)
586 case("BIAS_INITIALISER", "BIAS_INIT", "BIAS_INITIALIZER")
587 call assign_val(buffer, bias_initialiser_name, itmp1)
588 case("WEIGHTS")
589 1 kernel_initialiser_name = 'zeros'
590 1 bias_initialiser_name = 'zeros'
591 1 param_line = iline
592 case default
593
3/4
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 1 times.
46 if(scan(to_lower(trim(adjustl(buffer))),&
594 'abcdfghijklmnopqrstuvwxyz').eq.0)then
595 23 cycle tag_loop
596
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 elseif(tag(:3).eq.'END')then
597 23 cycle tag_loop
598 end if
599 write(err_msg,'("Unrecognised line in input file: ",A)') &
600 trim(adjustl(buffer))
601 call stop_program(err_msg)
602
9/12
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 23 times.
60 return
603 end select
604 end do tag_loop
605
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
1 kernel_initialiser = initialiser_setup(kernel_initialiser_name)
606
5/14
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
1 bias_initialiser = initialiser_setup(bias_initialiser_name)
607
608 call this%set_hyperparams( &
609 num_outputs = num_outputs, &
610 coord_dim = coord_dim, &
611 kernel_hidden = kernel_hidden, &
612 use_bias = use_bias, &
613 activation = activation, &
614 kernel_initialiser = kernel_initialiser, &
615 bias_initialiser = bias_initialiser, &
616 verbose = verbose_ &
617 1 )
618
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 call this%init(input_shape=[num_inputs, 0])
619
620
621 ! Read weights
622 !---------------------------------------------------------------------------
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(param_line.eq.0)then
624 write(0,*) "WARNING: WEIGHTS card in GRAPH_NOP not found"
625 else
626 1 call move(unit, param_line - iline, iostat=stat)
627
628
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 do p = 1, size(this%params)
629
10/20
✗ 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.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 3 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 3 times.
3 num_vals = size(this%params(p)%val(:,1))
630
7/14
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ 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 11 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 3 times.
3 allocate(data_list(num_vals), source=0._real32)
631 3 c = 1
632
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25 do while(c .le. num_vals)
633 22 read(unit,'(A)',iostat=stat) buffer
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if(stat.ne.0) exit
635 22 k = icount(buffer)
636
5/8
✗ Branch 1 not taken.
✓ Branch 2 taken 124 times.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 102 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 102 times.
124 read(buffer,*,iostat=stat) (data_list(j), j=c, c+k-1)
637 22 c = c + k
638 end do
639
15/28
✗ 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.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 3 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 3 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 3 times.
✗ Branch 33 not taken.
✓ Branch 34 taken 3 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 3 times.
✓ Branch 39 taken 102 times.
✓ Branch 40 taken 3 times.
105 this%params(p)%val(:,1) = data_list
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
4 deallocate(data_list)
641 end do
642
643 1 read(unit,'(A)') buffer
644
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if(trim(adjustl(buffer)).ne."END WEIGHTS")then
645 call stop_program("END WEIGHTS not where expected")
646 return
647 end if
648 end if
649
650 1 call move(unit, final_line - iline, iostat=stat)
651 1 read(unit,'(A)') buffer
652
3/6
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
2 if(trim(adjustl(buffer)).ne."END "//to_upper(trim(this%name)))then
653 write(err_msg,'("END ",A," not where expected")') &
654 to_upper(this%name)
655 call stop_program(err_msg)
656 1 return
657 end if
658
659
7/14
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
1 end subroutine read_gno
660 !###############################################################################
661
662
663 !###############################################################################
664 1 function read_graph_nop_layer(unit, verbose) result(layer)
665 !! Read a graph NOP layer from file and return
666 implicit none
667
668 ! Arguments
669 integer, intent(in) :: unit
670 !! Input unit number
671 integer, optional, intent(in) :: verbose
672 !! Verbosity level
673 class(base_layer_type), allocatable :: layer
674 !! Allocated base-layer instance containing the result
675
676 ! Local variables
677 integer :: verbose_ = 0
678 !! Effective verbosity level
679
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(present(verbose)) verbose_ = verbose
681 allocate(layer, source=graph_nop_layer_type( &
682
24/84
✗ Branch 1 not taken.
✓ Branch 2 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 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 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 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✓ Branch 47 taken 1 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 1 times.
✗ Branch 51 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 1 times.
✗ Branch 55 not taken.
✓ Branch 56 taken 1 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✓ Branch 60 taken 1 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 1 times.
✓ Branch 64 taken 1 times.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✓ Branch 67 taken 1 times.
✓ Branch 69 taken 1 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 1 times.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✓ Branch 74 taken 1 times.
✓ Branch 76 taken 1 times.
✗ Branch 77 not taken.
✓ Branch 78 taken 1 times.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✓ Branch 81 taken 1 times.
✓ Branch 83 taken 1 times.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✓ Branch 86 taken 1 times.
✗ Branch 87 not taken.
✓ Branch 88 taken 1 times.
✗ Branch 89 not taken.
✓ Branch 90 taken 1 times.
2 num_outputs=0, coord_dim=1))
683 1 call layer%read(unit, verbose=verbose_)
684
685 2 end function read_graph_nop_layer
686 !###############################################################################
687
688
689 !###############################################################################
690
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 subroutine update_message_gno(this, input)
691 !! Update message for the Graph Neural Operator layer
692 !!
693 !! Builds a differentiable computation graph through the diffstruc
694 !! autodiff framework:
695 !!
696 !! input(1,s) : node features [F_in x num_vertices]
697 !! input(2,s) : edge geometry / relative coords [coord_dim x num_edges]
698 !!
699 !! Pointer chain per sample s:
700 !! 1. edge_kernels = gno_kernel_eval(coords, kernel_params, adj)
701 !! 2. agg = gno_aggregate(features, edge_kernels, adj)
702 !! 3. bypass = matmul(W, features)
703 !! 4. z = agg + bypass
704 !! 5. z = add_bias(z, b) (if use_bias)
705 !! 6. output = activation(z)
706 implicit none
707
708 ! Arguments
709 class(graph_nop_layer_type), intent(inout), target :: this
710 !! Layer instance to execute
711 class(array_type), dimension(:,:), intent(in), target :: input
712 !! Input node-feature and edge-feature tensors
713
714 ! Local variables
715 integer :: s, F_in, F_out
716 !! Sample index and input/output feature counts
717 type(array_type), pointer :: ptr1, ptr2, ptr3, ptr4
718 !! Intermediate kernel, aggregate, bypass and combined tensors
719
720
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 F_in = this%num_vertex_features(0)
721
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 F_out = this%num_vertex_features(1)
722
723 ! Allocate output array
724
7/14
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 5 times.
5 if(size(input, 1) .lt. 2)then
725 call stop_program( &
726 'graph_nop layer expects vertex and edge feature inputs' &
727 )
728 return
729 end if
730
731
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if(allocated(this%output))then
732
12/22
✗ 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.
✓ Branch 20 taken 2 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 2 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 1 times.
3 if(any(shape(this%output).ne.[2, size(input,2)]))then
733 deallocate(this%output)
734 allocate(this%output(2, size(input,2)))
735 end if
736 else
737
25/46
✗ 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 taken 4 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 4 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 4 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 4 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 4 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 4 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 4 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 4 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 4 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 4 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 4 times.
✗ Branch 47 not taken.
✓ Branch 48 taken 4 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 4 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 4 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 4 times.
✓ Branch 59 taken 4 times.
✓ Branch 60 taken 4 times.
✓ Branch 61 taken 8 times.
✓ Branch 62 taken 4 times.
16 allocate(this%output(2, size(input,2)))
738 end if
739
740
8/14
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✓ Branch 18 taken 5 times.
✓ Branch 19 taken 5 times.
10 do s = 1, size(input, 2)
741
742 ! Step 1: Evaluate kernel MLP on every edge
743 ptr1 => gno_kernel_eval( &
744 input(2,s), & ! edge features [d, num_edges]
745 10 this%params(1), & ! packed kernel params
746 10 this%graph(s)%adj_ia, &
747 10 this%graph(s)%adj_ja, &
748 this%coord_dim, this%kernel_hidden, F_in, F_out &
749
9/18
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 5 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 5 times.
5 )
750
751 ! Step 2: Aggregate neighbour messages using per-edge kernels
752 ptr2 => gno_aggregate( &
753 input(1,s), & ! features [F_in, num_v]
754 ptr1, & ! edge kernels [F*F_in, num_edges]
755 10 this%graph(s)%adj_ia, &
756 10 this%graph(s)%adj_ja, &
757 F_in, F_out &
758
7/14
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
5 )
759
760 ! Step 3: Linear bypass — W @ features
761
5/10
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
5 ptr3 => matmul(this%params(2), input(1,s))
762
763 ! Step 4: Combine aggregation with bypass
764 5 ptr4 => ptr2 + ptr3
765
766 ! Step 5: Add bias (if used)
767
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if(this%use_bias)then
768 ptr4 => add_bias( &
769 10 ptr4, this%params(3), dim=1, dim_act_on_shape=.true. &
770
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 )
771 end if
772
773 ! Step 6: Apply activation
774 5 ptr4 => this%activation%apply(ptr4)
775
776 ! Store output
777
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
5 call this%output(1,s)%zero_grad()
778
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
5 call this%output(1,s)%assign_and_deallocate_source(ptr4)
779
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
5 this%output(1,s)%is_temporary = .false.
780
781
10/18
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 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.
5 if(this%output(2,s)%allocated) call this%output(2,s)%deallocate()
782
7/14
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 5 times.
5 call this%output(2,s)%allocate(source=input(2,s)%val)
783
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
5 call this%output(2,s)%zero_grad()
784
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
5 call this%output(2,s)%set_requires_grad(.false.)
785
4/8
✗ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
10 this%output(2,s)%is_temporary = .false.
786 end do
787
788 end subroutine update_message_gno
789 !###############################################################################
790
791
792 !###############################################################################
793 5 subroutine update_readout_gno(this)
794 !! No graph-level readout needed — GNO produces node-level output
795 implicit none
796
797 ! Arguments
798 class(graph_nop_layer_type), intent(inout), target :: this
799 !! Layer instance retained for interface compatibility
800 5 end subroutine update_readout_gno
801 !###############################################################################
802
803
79/167
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 16 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 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 taken 16 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 12 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✓ Branch 43 taken 12 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 12 times.
✓ Branch 46 taken 12 times.
✓ Branch 47 taken 14 times.
✓ Branch 48 taken 14 times.
✓ Branch 49 taken 14 times.
✓ Branch 50 taken 2 times.
✓ Branch 51 taken 14 times.
✓ Branch 52 taken 2 times.
✓ Branch 53 taken 12 times.
✓ Branch 54 taken 4 times.
✓ Branch 55 taken 14 times.
✓ Branch 56 taken 2 times.
✓ Branch 57 taken 40 times.
✓ Branch 58 taken 14 times.
✓ Branch 59 taken 40 times.
✗ Branch 60 not taken.
✓ Branch 61 taken 40 times.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✓ Branch 64 taken 40 times.
✗ Branch 65 not taken.
✓ Branch 66 taken 40 times.
✓ Branch 67 taken 12 times.
✓ Branch 68 taken 40 times.
✗ Branch 69 not taken.
✓ Branch 70 taken 52 times.
✓ Branch 71 taken 28 times.
✗ Branch 72 not taken.
✓ Branch 73 taken 3 times.
✓ Branch 74 taken 25 times.
✓ Branch 75 taken 3 times.
✓ Branch 76 taken 3 times.
✓ Branch 77 taken 16 times.
✓ Branch 78 taken 3 times.
✗ Branch 79 not taken.
✓ Branch 80 taken 17 times.
✓ Branch 81 taken 2 times.
✓ Branch 82 taken 17 times.
✓ Branch 83 taken 5 times.
✓ Branch 84 taken 14 times.
✓ Branch 85 taken 5 times.
✗ Branch 86 not taken.
✓ Branch 87 taken 3 times.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✓ Branch 90 taken 3 times.
✗ Branch 91 not taken.
✓ Branch 92 taken 3 times.
✗ Branch 93 not taken.
✓ Branch 94 taken 3 times.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✓ Branch 102 taken 3 times.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✓ Branch 109 taken 9 times.
✓ Branch 110 taken 3 times.
✓ Branch 111 taken 12 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 9 times.
✓ Branch 114 taken 3 times.
✓ Branch 115 taken 3 times.
✗ Branch 116 not taken.
✓ Branch 118 taken 12 times.
✗ Branch 119 not taken.
✗ Branch 120 not taken.
✓ Branch 121 taken 12 times.
✓ Branch 122 taken 12 times.
✗ Branch 123 not taken.
✗ Branch 124 not taken.
✓ Branch 125 taken 12 times.
✓ Branch 126 taken 12 times.
✗ Branch 127 not taken.
✗ Branch 128 not taken.
✓ Branch 129 taken 12 times.
✓ Branch 130 taken 12 times.
✗ Branch 131 not taken.
✓ Branch 132 taken 2 times.
✓ Branch 133 taken 10 times.
✓ Branch 134 taken 12 times.
✗ Branch 135 not taken.
✗ Branch 136 not taken.
✓ Branch 137 taken 12 times.
✓ Branch 139 taken 12 times.
✗ Branch 140 not taken.
✗ Branch 141 not taken.
✓ Branch 142 taken 12 times.
✗ Branch 143 not taken.
✓ Branch 144 taken 12 times.
✓ Branch 146 taken 12 times.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✓ Branch 149 taken 12 times.
✗ Branch 150 not taken.
✓ Branch 151 taken 12 times.
✓ Branch 153 taken 12 times.
✗ Branch 154 not taken.
✗ Branch 155 not taken.
✓ Branch 156 taken 12 times.
✗ Branch 157 not taken.
✓ Branch 158 taken 12 times.
✓ Branch 160 taken 12 times.
✗ Branch 161 not taken.
✗ Branch 162 not taken.
✓ Branch 163 taken 12 times.
✓ Branch 164 taken 12 times.
✗ Branch 165 not taken.
✗ Branch 166 not taken.
✓ Branch 167 taken 12 times.
✓ Branch 168 taken 12 times.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✓ Branch 171 taken 12 times.
194 end module athena__graph_nop_layer
804