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