Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TMVA_SOFIE_GNN_Parser.py
Go to the documentation of this file.
1## \file
2## \ingroup tutorial_ml
3## \notebook -nodraw
4##
5## Tutorial showing how to parse a GNN from GraphNet and make a SOFIE model
6## The tutorial also generate some data which can serve as input for the tutorial TMVA_SOFIE_GNN_Application.C
7##
8## \macro_code
9
10import ROOT
11
12import numpy as np
13import graph_nets as gn
14from graph_nets import utils_tf
15import sonnet as snt
16#for getting time and memory
17import time
18import os
19import psutil
20
21# defining graph properties. Number of edges/modes are the maximum
22num_max_nodes=100
23num_max_edges=300
24node_size=4
25edge_size=4
26global_size=1
27LATENT_SIZE = 100
28NUM_LAYERS = 4
29processing_steps = 5
30numevts = 100
31
32verbose = False
33
34#print the used memory in MB
printMemory(s = "") :
36 #get memory of current process
37 pid = os.getpid()
38 python_process = psutil.Process(pid)
39 memoryUse = python_process.memory_info()[0]/(1024.*1024.) #divide by 1024 * 1024 to get memory in MB
40 print(s,"memory:",memoryUse,"(MB)")
41
42
43# method for returning dictionary of graph data
get_dynamic_graph_data_dict(NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2, GLOBAL_FEATURE_SIZE=1):
45 num_nodes = np.random.randint(num_max_nodes-2, size=1)[0] + 2
46 num_edges = np.random.randint(num_max_edges-1, size=1)[0] + 1
47 return {
48 "globals": 10*np.random.rand(GLOBAL_FEATURE_SIZE).astype(np.float32)-5.,
49 "nodes": 10*np.random.rand(num_nodes, NODE_FEATURE_SIZE).astype(np.float32)-5.,
50 "edges": 10*np.random.rand(num_edges, EDGE_FEATURE_SIZE).astype(np.float32)-5.,
51 "senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
52 "receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32)
53 }
54
55# generate graph data with a fixed number of nodes/edges
get_fix_graph_data_dict(num_nodes, num_edges, NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2, GLOBAL_FEATURE_SIZE=1):
57 return {
58 "globals": np.ones((GLOBAL_FEATURE_SIZE),dtype=np.float32),
59 "nodes": np.ones((num_nodes, NODE_FEATURE_SIZE), dtype = np.float32),
60 "edges": np.ones((num_edges, EDGE_FEATURE_SIZE), dtype = np.float32),
61 "senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int32),
62 "receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int32)
63 }
64
65
66
67
68# method to instantiate mlp model to be added in GNN
make_mlp_model():
70 return snt.Sequential([
71 snt.nets.MLP([LATENT_SIZE]*NUM_LAYERS, activate_final=True),
72 snt.LayerNorm(axis=-1, create_offset=True, create_scale=True)
73 ])
74
75# defining GraphIndependent class with MLP edge, node, and global models.
76class MLPGraphIndependent(snt.Module):
77 def __init__(self, name="MLPGraphIndependent"):
78 super(MLPGraphIndependent, self).__init__(name=name)
80 edge_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*NUM_LAYERS, activate_final=True),
81 node_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*NUM_LAYERS, activate_final=True),
82 global_model_fn = lambda: snt.nets.MLP([LATENT_SIZE]*NUM_LAYERS, activate_final=True))
83
84 def __call__(self, inputs):
85 return self._network(inputs)
86
87# defining Graph network class with MLP edge, node, and global models.
88class MLPGraphNetwork(snt.Module):
89 def __init__(self, name="MLPGraphNetwork"):
90 super(MLPGraphNetwork, self).__init__(name=name)
92 edge_model_fn=make_mlp_model,
93 node_model_fn=make_mlp_model,
94 global_model_fn=make_mlp_model)
95
96 def __call__(self, inputs):
97 return self._network(inputs)
98
99# defining a Encode-Process-Decode module for LHCb toy model
100class EncodeProcessDecode(snt.Module):
101
102 def __init__(self,
103 name="EncodeProcessDecode"):
104 super(EncodeProcessDecode, self).__init__(name=name)
109
110 def __call__(self, input_op, num_processing_steps):
111 latent = self._encoder(input_op)
112 latent0 = latent
113 output_ops = []
114 for _ in range(num_processing_steps):
115 core_input = utils_tf.concat([latent0, latent], axis=1)
116 latent = self._core(core_input)
117 decoded_op = self._decoder(latent)
118 output_ops.append(self._output_transform(decoded_op))
119 return output_ops
120
121
122########################################################################################################
123
124# Instantiating EncodeProcessDecode Model
125
126printMemory("before instantiating")
EncodeProcessDecode()
128printMemory("after instantiating")
129
130# Initializing randomized input data with maximum number of nodes/edges
get_fix_graph_data_dict(num_max_nodes, num_max_edges, node_size, edge_size, global_size)
132
133#input_graphs is a tuple representing the initial data
utils_tf.data_dicts_to_graphs_tuple([GraphData])
135
136# Initializing randomized input data for core
137# note that the core network has as input a double number of features
get_fix_graph_data_dict(num_max_nodes, num_max_edges, 2*LATENT_SIZE, 2*LATENT_SIZE, 2*LATENT_SIZE)
utils_tf.data_dicts_to_graphs_tuple([CoreGraphData])
140
141#initialize graph data for decoder (input is LATENT_SIZE)
get_fix_graph_data_dict(num_max_nodes, num_max_edges, LATENT_SIZE, LATENT_SIZE, LATENT_SIZE)
143
144# Make prediction of GNN. This will initialize the GNN with weights
145printMemory("before first eval")
ep_model(input_graph_data, processing_steps)
147printMemory("after first eval")
148#print("---> Input:\n",input_graph_data)
149#print("\n\n------> Input core data:\n",input_core_graph_data)
150#print("\n\n---> Output:\n",output_gn)
151
152# Make SOFIE Model, the model will be made using a maximum number of nodes/edges which are inside GraphData
153
ep_model._encoder._network, GraphData, filename = "encoder")
157
ep_model._core._network, CoreGraphData, filename = "core")
161
ep_model._decoder._network, DecodeGraphData, filename = "decoder")
165
ep_model._output_transform._network, DecodeGraphData, filename = "output_transform")
169
170####################################################################################################################################
171
172#generate data and save in a ROOT TTree
173#
174
ROOT.TFile.Open("graph_data.root","RECREATE")
ROOT.TTree("gdata","GNN data")
177#need to store each element since annot store RTensor
178
ROOT.std.vector['float'](num_max_nodes*node_size)
ROOT.std.vector['float'](num_max_edges*edge_size)
ROOT.std.vector['float'](global_size)
ROOT.std.vector['int'](num_max_edges)
ROOT.std.vector['int'](num_max_edges)
ROOT.std.vector['float'](3)
185
186tree.Branch("node_data", "std::vector<float>" , node_data)
187tree.Branch("edge_data", "std::vector<float>" , edge_data)
188tree.Branch("global_data", "std::vector<float>" , global_data)
189tree.Branch("receivers", "std::vector<int>" , receivers)
190tree.Branch("senders", "std::vector<int>" , senders)
191
192
193print("\n\nSaving data in a ROOT File:")
ROOT.TH1D("h1","GraphNet nodes output",40,1,0)
ROOT.TH1D("h2","GraphNet edges output",40,1,0)
ROOT.TH1D("h3","GraphNet global output",40,1,0)
197dataset = []
198for i in range(0,numevts):
get_dynamic_graph_data_dict(node_size, edge_size, global_size)
200 s_nodes = graphData['nodes'].size
201 s_edges = graphData['edges'].size
202 num_edges = graphData['edges'].shape[0]
reshape((graphData['nodes'].size)))
205 tmp = ROOT.std.vector['float'](graphData['edges'].reshape((graphData['edges'].size)))
207 tmp = ROOT.std.vector['float'](graphData['globals'].reshape((graphData['globals'].size)))
209 #make sure dtype of graphData['receivers'] and senders is int32
210 tmp = ROOT.std.vector['int'](graphData['receivers'])
212 tmp = ROOT.std.vector['int'](graphData['senders'])
214 if (i < 1 and verbose) :
215 print("Nodes - shape:",int(node_data.size()/node_size),node_size,"data: ",node_data)
216 print("Edges - shape:",num_edges, edge_size,"data: ", edge_data)
217 print("Globals : ",global_data)
218 print("Receivers : ",receivers)
219 print("Senders : ",senders)
220#
221#evaluate graph net on these events
222#
223 tree.Fill()
utils_tf.data_dicts_to_graphs_tuple([graphData])
225 dataset.append(tf_graph_data)
226
228
229#do a first evaluation
230printMemory("before eval1")
ep_model(dataset[0], processing_steps)
232printMemory("after eval1")
233
time.time()
235firstEvent = True
236for tf_graph_data in dataset:
237 output_gnn = ep_model(tf_graph_data, processing_steps)
nodes.numpy()
edges.numpy()
globals.numpy()
241 outgnn[0] = np.mean(output_nodes)
242 outgnn[1] = np.mean(output_edges)
243 outgnn[2] = np.mean(output_globals)
244 h1.Fill(outgnn[0])
245 h2.Fill(outgnn[1])
246 h3.Fill(outgnn[2])
247 if (firstEvent and verbose) :
248 print("Output of first event")
249 print("nodes data", output_gnn[-1].nodes.numpy())
250 print("edge data", output_gnn[-1].edges.numpy())
251 print("global data", output_gnn[-1].globals.numpy())
252 firstEvent = False
253
254
time.time()
256
257print("time to evaluate events",end-start)
258printMemory("after eval Nevts")
259
ROOT.TCanvas()
261c1.Divide(1,3)
262c1.cd(1)
264c1.cd(2)
266c1.cd(3)
268
270h1.Write()
271h2.Write()
272h3.Write()
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
__init__(self, name="EncodeProcessDecode")
__call__(self, input_op, num_processing_steps)
__init__(self, name="MLPGraphIndependent")
__init__(self, name="MLPGraphNetwork")
get_dynamic_graph_data_dict(NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2, GLOBAL_FEATURE_SIZE=1)
get_fix_graph_data_dict(num_nodes, num_edges, NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2, GLOBAL_FEATURE_SIZE=1)