Skip to content

Commit 3c17793

Browse files
sanjibansglmoneta
andcommitted
[tmva][sofie-gnn] Fix on loading required packages (sonnet, graph_nets) and restricting numpy version
avoid trying to load sonnet and graph_nets if not installed [tmva][sofie-gnn] numpy version for sofie-gnn test should be restricted within <=1.19 or >=1.24 Because of the changed behavior of np.bool and similar aliases for builtin data types, we need to restrict the numpy version to the stated range for sonnet. For more information, refer here: numpy/numpy#14882 numpy/numpy#22607 fix: definition of OutputGenerated in RModel_Base [tmva][sofie-gnn] Suppress warnings for cases other than .dat file in method WriteInitializedTensorsToFile in RModel [tmva][sofie-gnn] Fix node update in GNN and size of global features in GraphIndependent [tmva][sofie-gnn] Fix node update in RModel_GNN generated code [tmva][sofie-gnn] Fix for correct size of global features in GraphIndependent fix also the way the computation of output features in RModel_GNN Fix dimension of global feature tensor during node update If the number of nodes is larger than the edges the tensor storing the global feature needs to be resize to the correct number of nodes * number of feature [tmva][sofie-gnn] Fix importing _gnn if python version is less than 3.8 Improve also gnn test and address some of the Vincenzo's comments Changes addressing comments by @vepadulano Co-authored-by: moneta <lorenzo.moneta@cern.ch>
1 parent 2f2b9c6 commit 3c17793

28 files changed

+546
-429
lines changed

bindings/pyroot/pythonizations/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ if(tmva)
6868
endif()
6969

7070
if(PYTHON_VERSION_STRING_Development_Main VERSION_GREATER_EQUAL 3.8 AND dataframe)
71-
list(APPEND PYROOT_EXTRA_PY3_SOURCE
71+
list(APPEND PYROOT_EXTRA_PY3_SOURCE
7272
ROOT/_pythonization/_tmva/_batchgenerator.py)
7373
endif()
7474

bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tmva/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ def inject_rbatchgenerator(ns):
4141
setattr(ns.Experimental, func_name, python_func)
4242

4343
return ns
44-
45-
from ._gnn import RModel_GNN, RModel_GraphIndependent
44+
45+
from ._gnn import RModel_GNN, RModel_GraphIndependent
4646

4747
hasRDF = gSystem.GetFromPipe("root-config --has-dataframe") == "yes"
4848
if hasRDF:

bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tmva/_gnn.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import sys
1515
from cppyy import gbl as gbl_namespace
1616

17-
if sys.version_info < (3, 7):
17+
if sys.version_info < (3, 8):
1818
raise RuntimeError("GNN Pythonizations are only supported in Python3")
1919

2020

@@ -29,7 +29,7 @@ def getActivationFunction(model):
2929
3030
Returns:
3131
The activation function enum value.
32-
"""
32+
"""
3333
function = model._activation.__name__
3434
if function == 'relu':
3535
return gbl_namespace.TMVA.Experimental.SOFIE.Activation.RELU
@@ -80,7 +80,7 @@ def add_layer_norm(gin, module_layer, function_target):
8080
else:
8181
model_block = gin.globals_update_block
8282
axis = module_layer._axis
83-
eps = module_layer._eps
83+
eps = module_layer._eps
8484
stash_type = 1
8585
name_x = model_block.GetFunctionBlock().GetOutputTensorNames()[0]
8686
name_bias = module_layer.offset.name
@@ -90,7 +90,7 @@ def add_layer_norm(gin, module_layer, function_target):
9090
current_output_tensors = model_block.GetFunctionBlock().GetOutputTensorNames()
9191
new_output_tensors = gbl_namespace.std.vector['std::string']()
9292
new_output_tensors.push_back(name_Y)
93-
model_block.GetFunctionBlock().AddOutputTensorNameList(new_output_tensors)
93+
model_block.GetFunctionBlock().AddOutputTensorNameList(new_output_tensors)
9494

9595
def add_weights(gin, weights, function_target):
9696
"""
@@ -133,12 +133,12 @@ def add_aggregate_function(gin, reducer, relation):
133133
agg = gbl_namespace.TMVA.Experimental.SOFIE.RFunction_Mean()
134134
gin.createAggregateFunction[gbl_namespace.TMVA.Experimental.SOFIE.RFunction_Mean](agg, relation)
135135
else:
136-
raise RuntimeError("Invalid aggregate function for reduction")
136+
raise RuntimeError("Invalid aggregate function for reduction")
137137

138138

139139
def add_update_function(gin, component_model, graph_type, function_target):
140140
"""
141-
Add update function for respective function target, either of nodes, edges or globals
141+
Add update function for respective function target, either of nodes, edges or globals
142142
based on the supplied component_model
143143
144144
Parameters:
@@ -164,7 +164,7 @@ def add_update_function(gin, component_model, graph_type, function_target):
164164

165165

166166

167-
class RModel_GNN:
167+
class RModel_GNN:
168168
"""
169169
Wrapper class for graph_nets' GNN model;s parsing and inference generation
170170
@@ -199,21 +199,21 @@ def ParseFromMemory(graph_module, graph_data, filename = "gnn_network"):
199199
gin.num_global_features = len(graph_data['globals'])
200200

201201
gin.filename = filename
202-
202+
203203
# adding the node update function
204204
node_model = graph_module._node_block._node_model
205-
add_update_function(gin, node_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
205+
add_update_function(gin, node_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
206206
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.NODES)
207207

208208
# adding the edge update function
209209
edge_model = graph_module._edge_block._edge_model
210-
add_update_function(gin, edge_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
210+
add_update_function(gin, edge_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
211211
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.EDGES)
212212

213213
# adding the global update function
214214
global_model = graph_module._global_block._global_model
215-
add_update_function(gin, global_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
216-
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.GLOBALS)
215+
add_update_function(gin, global_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GNN,
216+
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.GLOBALS)
217217

218218
# adding edge-node aggregate function
219219
add_aggregate_function(gin, graph_module._node_block._received_edges_aggregator._reducer.__qualname__, gbl_namespace.TMVA.Experimental.SOFIE.FunctionRelation.NODES_EDGES)
@@ -274,18 +274,18 @@ def ParseFromMemory(graph_module, graph_data, filename = "graph_independent_netw
274274

275275
# adding the node update function
276276
node_model = graph_module._node_model._model
277-
add_update_function(gin, node_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
277+
add_update_function(gin, node_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
278278
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.NODES)
279279

280280
# adding the edge update function
281281
edge_model = graph_module._edge_model._model
282-
add_update_function(gin, edge_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
282+
add_update_function(gin, edge_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
283283
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.EDGES)
284284

285285
# adding the global update function
286286
global_model = graph_module._global_model._model
287-
add_update_function(gin, global_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
288-
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.GLOBALS)
287+
add_update_function(gin, global_model, gbl_namespace.TMVA.Experimental.SOFIE.GraphType.GraphIndependent,
288+
gbl_namespace.TMVA.Experimental.SOFIE.FunctionTarget.GLOBALS)
289289

290290
graph_independent_model = gbl_namespace.TMVA.Experimental.SOFIE.RModel_GraphIndependent(gin)
291291
blas_routines = gbl_namespace.std.vector['std::string']()

bindings/pyroot/pythonizations/test/CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,16 @@ endif()
128128
# SOFIE-GNN pythonizations
129129
if (dataframe AND tmva)
130130
if(NOT MSVC OR CMAKE_SIZEOF_VOID_P EQUAL 4 OR win_broken_tests AND PYTHON_VERSION_MAJOR_Development_Main EQUAL 3 )
131-
ROOT_ADD_PYUNITTEST(pyroot_pyz_sofie_gnn sofie_gnn.py PYTHON_DEPS numpy sonnet graph_nets)
131+
find_python_module(sonnet QUIET)
132+
find_python_module(graph_nets QUIET)
133+
if (PY_SONNET_FOUND AND PY_GRAPH_NETS_FOUND)
134+
ROOT_ADD_PYUNITTEST(pyroot_pyz_sofie_gnn sofie_gnn.py PYTHON_DEPS numpy sonnet graph_nets)
135+
endif()
132136
endif()
133137
endif()
134138

135139
# RTensor pythonizations
136-
if (tmva AND sofie)
140+
if (tmva)
137141
if(NOT MSVC OR CMAKE_SIZEOF_VOID_P EQUAL 4 OR win_broken_tests)
138142
ROOT_ADD_PYUNITTEST(pyroot_pyz_rtensor rtensor.py PYTHON_DEPS numpy)
139143
endif()

bindings/pyroot/pythonizations/test/sofie_gnn.py

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import numpy as np
55
from numpy.testing import assert_almost_equal
66

7-
if np.__version__ > "1.19":
8-
raise RuntimeError(f"This test requires NumPy version 1.19 or lower")
7+
if np.__version__ >= "1.20" and np.__version__ < "1.24":
8+
raise RuntimeError(f"This test requires NumPy version <=1.19 or >=1.24")
99

1010
import graph_nets as gn
1111
from graph_nets import utils_tf
1212
import sonnet as snt
13+
import os
1314

1415

1516

@@ -21,9 +22,22 @@ def get_graph_data_dict(num_nodes, num_edges, GLOBAL_FEATURE_SIZE=2, NODE_FEATUR
2122
"nodes": np.random.rand(num_nodes, NODE_FEATURE_SIZE).astype(np.float32),
2223
"edges": np.random.rand(num_edges, EDGE_FEATURE_SIZE).astype(np.float32),
2324
"senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
24-
"receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
25+
"receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32)
2526
}
2627

28+
def resize_graph_data(input_data, GLOBAL_FEATURE_SIZE=2, NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2) :
29+
n = input_data["nodes"]
30+
e = input_data["edges"]
31+
s = input_data["senders"]
32+
r = input_data["receivers"]
33+
return {
34+
"globals" : np.zeros((GLOBAL_FEATURE_SIZE )),
35+
"nodes" : np.zeros((n.shape[0],NODE_FEATURE_SIZE )),
36+
"edges" : np.zeros((e.shape[0],EDGE_FEATURE_SIZE )),
37+
"senders" : s, "receivers" : r
38+
}
39+
40+
LATENT_SIZE = 2
2741
def make_mlp_model():
2842
"""Instantiates a new MLP, followed by LayerNorm.
2943
@@ -34,7 +48,7 @@ def make_mlp_model():
3448
A Sonnet module which contains the MLP and LayerNorm.
3549
"""
3650
return snt.Sequential([
37-
snt.nets.MLP([2,2], activate_final=True),
51+
snt.nets.MLP([LATENT_SIZE]*2, activate_final=True),
3852
snt.LayerNorm(axis=-1, create_offset=True, create_scale=True)
3953
])
4054

@@ -48,9 +62,9 @@ class MLPGraphIndependent(snt.Module):
4862
def __init__(self, name="MLPGraphIndependent"):
4963
super(MLPGraphIndependent, self).__init__(name=name)
5064
self._network = gn.modules.GraphIndependent(
51-
edge_model_fn = lambda: snt.nets.MLP([2,2], activate_final=True),
52-
node_model_fn = lambda: snt.nets.MLP([2,2], activate_final=True),
53-
global_model_fn = lambda: snt.nets.MLP([2,2], activate_final=True))
65+
edge_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*2, activate_final=True),
66+
node_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*2, activate_final=True),
67+
global_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*2, activate_final=True))
5468

5569
def __call__(self, inputs):
5670
return self._network(inputs)
@@ -69,12 +83,19 @@ def __init__(self, name="MLPGraphNetwork"):
6983
def __call__(self, inputs):
7084
return self._network(inputs)
7185

86+
def PrintGData(data, printShape = True):
87+
n = data.nodes.numpy()
88+
e = data.edges.numpy()
89+
g = data.globals.numpy()
90+
if (printShape) :
91+
print("GNet data ... shapes",n.shape,e.shape,g.shape)
92+
print(" node data", n.reshape(n.size,))
93+
print(" edge data", e.reshape(e.size,))
94+
print(" global data",g.reshape(g.size,))
95+
7296
class EncodeProcessDecode(snt.Module):
7397

7498
def __init__(self,
75-
edge_output_size=None,
76-
node_output_size=None,
77-
global_output_size=None,
7899
name="EncodeProcessDecode"):
79100
super(EncodeProcessDecode, self).__init__(name=name)
80101
self._encoder = MLPGraphIndependent()
@@ -103,12 +124,15 @@ def test_parse_gnn(self):
103124
Test that parsed GNN model from a graphnets model generates correct
104125
inference code
105126
'''
127+
128+
print('\nRun Graph parsing test')
129+
106130
GraphModule = gn.modules.GraphNetwork(
107131
edge_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True),
108132
node_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True),
109133
global_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True))
110134

111-
GraphData = get_graph_data_dict(2,1)
135+
GraphData = get_graph_data_dict(2,1,2,2,2)
112136
input_graphs = utils_tf.data_dicts_to_graphs_tuple([GraphData])
113137
output = GraphModule(input_graphs)
114138

@@ -135,18 +159,26 @@ def test_parse_gnn(self):
135159
assert_almost_equal(output_edge_data, np.asarray(input_data.edge_data))
136160
assert_almost_equal(output_global_data, np.asarray(input_data.global_data))
137161

162+
fname = "gnn_network"
163+
os.remove(fname + '.dat')
164+
os.remove(fname + '.hxx')
165+
138166

139167
def test_parse_graph_independent(self):
140168
'''
141169
Test that parsed GraphIndependent model from a graphnets model generates correct
142170
inference code
143171
'''
172+
173+
print('\nRun Graph Independent parsing test')
174+
175+
144176
GraphModule = gn.modules.GraphIndependent(
145177
edge_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True),
146178
node_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True),
147179
global_model_fn=lambda: snt.nets.MLP([2,2], activate_final=True))
148180

149-
GraphData = get_graph_data_dict(2,1)
181+
GraphData = get_graph_data_dict(2,1,2,2,2)
150182
input_graphs = utils_tf.data_dicts_to_graphs_tuple([GraphData])
151183
output = GraphModule(input_graphs)
152184

@@ -173,39 +205,58 @@ def test_parse_graph_independent(self):
173205
assert_almost_equal(output_edge_data, np.asarray(input_data.edge_data))
174206
assert_almost_equal(output_global_data, np.asarray(input_data.global_data))
175207

208+
fname = "graph_independent_network"
209+
os.remove(fname + '.dat')
210+
os.remove(fname + '.hxx')
211+
176212
def test_lhcb_toy_inference(self):
177213
'''
178214
Test that parsed stack of SOFIE GNN and GraphIndependent modules generate the correct
179215
inference code
180216
'''
217+
218+
print('Run LHCb test')
219+
181220
# Instantiating EncodeProcessDecode Model
182-
ep_model = EncodeProcessDecode(2,2,2)
221+
222+
#number of features for node. edge, globals
223+
nsize = 3
224+
esize = 3
225+
gsize = 2
226+
lsize = LATENT_SIZE #hard-coded latent size in definition of GNET model (for node edge and globals)
227+
228+
ep_model = EncodeProcessDecode()
183229

184230
# Initializing randomized input data
185-
GraphData = get_graph_data_dict(2,1)
186-
input_graphs = utils_tf.data_dicts_to_graphs_tuple([GraphData])
231+
InputGraphData = get_graph_data_dict(2,1, gsize, nsize, esize)
232+
input_graphs = utils_tf.data_dicts_to_graphs_tuple([InputGraphData])
233+
234+
# Make data for core networks (number of features for node/edge global is 2 * lsize)
235+
CoreGraphData = resize_graph_data(InputGraphData, 2 * lsize, 2 * lsize, 2 * lsize)
236+
237+
238+
OutputGraphData = resize_graph_data(InputGraphData, lsize, lsize, lsize)
187239

188-
# Initializing randomized input data for core
189-
CoreGraphData = get_graph_data_dict(2, 1, 4, 4, 4)
190-
input_graphs_2 = utils_tf.data_dicts_to_graphs_tuple([CoreGraphData])
191240

192241
# Collecting output from GraphNets model stack
193242
output_gn = ep_model(input_graphs, 2)
194243

244+
print("senders and receivers ",InputGraphData['senders'],InputGraphData['receivers'])
245+
195246
# Declaring sofie models
196-
encoder = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._encoder._network, GraphData, filename = "encoder")
247+
encoder = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._encoder._network, InputGraphData, filename = "encoder")
197248
encoder.Generate()
198249
encoder.OutputGenerated()
199250

200251
core = ROOT.TMVA.Experimental.SOFIE.RModel_GNN.ParseFromMemory(ep_model._core._network, CoreGraphData, filename = "core")
201252
core.Generate()
202253
core.OutputGenerated()
203254

204-
decoder = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._decoder._network, GraphData, filename = "decoder")
255+
decoder = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._decoder._network, OutputGraphData, filename = "decoder")
205256
decoder.Generate()
206257
decoder.OutputGenerated()
207258

208-
output_transform = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._output_transform._network, GraphData, filename = "output_transform")
259+
output_transform = ROOT.TMVA.Experimental.SOFIE.RModel_GraphIndependent.ParseFromMemory(ep_model._output_transform._network, OutputGraphData, filename = "output_transform")
209260
output_transform.Generate()
210261
output_transform.OutputGenerated()
211262

@@ -222,14 +273,18 @@ def test_lhcb_toy_inference(self):
222273

223274
# Preparing the input data for running inference on sofie
224275
input_data = ROOT.TMVA.Experimental.SOFIE.GNN_Data()
225-
input_data.node_data = ROOT.TMVA.Experimental.AsRTensor(GraphData['nodes'])
226-
input_data.edge_data = ROOT.TMVA.Experimental.AsRTensor(GraphData['edges'])
227-
input_data.global_data = ROOT.TMVA.Experimental.AsRTensor(GraphData['globals'])
276+
input_data.node_data = ROOT.TMVA.Experimental.AsRTensor(InputGraphData['nodes'])
277+
input_data.edge_data = ROOT.TMVA.Experimental.AsRTensor(InputGraphData['edges'])
278+
input_data.global_data = ROOT.TMVA.Experimental.AsRTensor(InputGraphData['globals'])
279+
280+
output_gn = ep_model(input_graphs, 2)
228281

229282
# running inference on sofie
230-
encoder_session.infer(input_data)
231-
latent0 = CopyData(input_data)
232-
latent = input_data
283+
data = CopyData(input_data)
284+
285+
encoder_session.infer(data)
286+
latent0 = CopyData(data)
287+
latent = data
233288
output_ops = []
234289
for _ in range(2):
235290
core_input = ROOT.TMVA.Experimental.SOFIE.Concatenate(latent0, latent, axis=1)
@@ -246,10 +301,16 @@ def test_lhcb_toy_inference(self):
246301
output_global_data = output_gn[i].globals.numpy().flatten()
247302

248303
assert_almost_equal(output_node_data, np.asarray(output_ops[i].node_data))
304+
249305
assert_almost_equal(output_edge_data, np.asarray(output_ops[i].edge_data))
250-
assert_almost_equal(output_global_data, np.asarray(output_ops[i].global_data))
251306

307+
assert_almost_equal(output_global_data, np.asarray(output_ops[i].global_data))
252308

309+
#remove header files after being used
310+
filesToRemove = ['core','encoder','decoder','output_transform']
311+
for fname in filesToRemove:
312+
os.remove(fname + '.hxx')
313+
os.remove(fname + '.dat')
253314

254315

255316
if __name__ == '__main__':

tmva/pymva/test/EmitFromKeras.cxx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// The program is run when the target 'TestRModelParserKeras' is built.
55
// The program generates the required .hxx file after parsing a Keras .keras file into a RModel object.
66

7-
#include "TMVA/RModel_Base.hxx"
87
#include "TMVA/RModel.hxx"
98
#include "TMVA/RModelParser_Keras.h"
109

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy