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