38torch.set_grad_enabled(
False)
42def get_dynamic_graph_data_dict(NODE_FEATURE_SIZE=2, EDGE_FEATURE_SIZE=2, GLOBAL_FEATURE_SIZE=1):
43 num_nodes = np.random.randint(num_max_nodes - 2, size=1)[0] + 2
44 num_edges = np.random.randint(num_max_edges - 2, size=1)[0] + 2
46 "globals": 10 * np.random.rand(1, GLOBAL_FEATURE_SIZE).astype(np.float32) - 5.0,
47 "nodes": 10 * np.random.rand(num_nodes, NODE_FEATURE_SIZE).astype(np.float32) - 5.0,
48 "edges": 10 * np.random.rand(num_edges, EDGE_FEATURE_SIZE).astype(np.float32) - 5.0,
49 "senders": np.random.randint(num_nodes, size=num_edges, dtype=np.int64),
50 "receivers": np.random.randint(num_nodes, size=num_edges, dtype=np.int64),
56def make_mlp_model(num_inputs, with_layer_norm=False):
58 for _
in range(NUM_LAYERS):
59 layers += [nn.Linear(num_inputs, LATENT_SIZE), nn.ReLU()]
60 num_inputs = LATENT_SIZE
62 layers.append(nn.LayerNorm(LATENT_SIZE))
63 return nn.Sequential(*layers)
67class MLPGraphIndependent(nn.Module):
68 def __init__(self, num_node_inputs, num_edge_inputs, num_global_inputs):
70 self.node_fn = make_mlp_model(num_node_inputs)
71 self.edge_fn = make_mlp_model(num_edge_inputs)
72 self.global_fn = make_mlp_model(num_global_inputs)
74 def forward(self, node_data, edge_data, global_data):
75 return self.node_fn(node_data), self.edge_fn(edge_data), self.global_fn(global_data)
82class MLPGraphNetwork(nn.Module):
83 def __init__(self, num_node_inputs, num_edge_inputs, num_global_inputs):
85 self.edge_fn = make_mlp_model(num_edge_inputs + 2 * num_node_inputs + num_global_inputs,
True)
86 self.node_fn = make_mlp_model(LATENT_SIZE + num_node_inputs + num_global_inputs,
True)
87 self.global_fn = make_mlp_model(2 * LATENT_SIZE + num_global_inputs,
True)
89 def forward(self, node_data, edge_data, global_data, receivers, senders):
90 n_nodes = node_data.shape[0]
91 n_edges = edge_data.shape[0]
92 edge_input = torch.cat(
93 [edge_data, node_data[receivers], node_data[senders], global_data.expand(n_edges, -1)], dim=1
95 edge_output = self.edge_fn(edge_input)
97 received_edges = torch.zeros(n_nodes, edge_output.shape[1]).scatter_add(
98 0, receivers.unsqueeze(1).expand(n_edges, edge_output.shape[1]), edge_output
100 node_input = torch.cat([received_edges, node_data, global_data.expand(n_nodes, -1)], dim=1)
101 node_output = self.node_fn(node_input)
102 global_input = torch.cat(
103 [edge_output.sum(0, keepdim=
True), node_output.sum(0, keepdim=
True), global_data], dim=1
105 global_output = self.global_fn(global_input)
106 return node_output, edge_output, global_output
110class EncodeProcessDecode(nn.Module):
113 self._encoder = MLPGraphIndependent(node_size, edge_size, global_size)
114 self._core = MLPGraphNetwork(2 * LATENT_SIZE, 2 * LATENT_SIZE, 2 * LATENT_SIZE)
115 self._decoder = MLPGraphIndependent(LATENT_SIZE, LATENT_SIZE, LATENT_SIZE)
116 self._output_transform = MLPGraphIndependent(LATENT_SIZE, LATENT_SIZE, LATENT_SIZE)
118 def forward(self, node_data, edge_data, global_data, receivers, senders, num_processing_steps):
119 latent = self._encoder(node_data, edge_data, global_data)
122 for _
in range(num_processing_steps):
123 core_input = tuple(torch.cat([a, b], dim=1)
for a, b
in zip(latent0, latent))
124 latent = self._core(*core_input, receivers, senders)
125 decoded_op = self._decoder(*latent)
126 output_ops.append(self._output_transform(*decoded_op))
133ep_model = EncodeProcessDecode()
137num_nodes_dim = torch.export.Dim(
"num_nodes", min=2, max=num_max_nodes)
138num_edges_dim = torch.export.Dim(
"num_edges", min=2, max=num_max_edges)
141def export_component(component, name, num_features):
143 torch.zeros(num_max_nodes, num_features[0]),
144 torch.zeros(num_max_edges, num_features[1]),
145 torch.zeros(1, num_features[2]),
147 input_names = [
"node_data",
"edge_data",
"global_data"]
149 "node_data": {0: num_nodes_dim},
150 "edge_data": {0: num_edges_dim},
153 if isinstance(component, MLPGraphNetwork):
155 torch.randint(num_max_nodes, (num_max_edges,)),
156 torch.randint(num_max_nodes, (num_max_edges,)),
158 input_names += [
"receivers",
"senders"]
159 dynamic_shapes.update({
"receivers": {0: num_edges_dim},
"senders": {0: num_edges_dim}})
164 input_names=input_names,
165 output_names=[
"node_output",
"edge_output",
"global_output"],
166 dynamic_shapes=dynamic_shapes,
171export_component(ep_model._encoder,
"encoder", (node_size, edge_size, global_size))
172export_component(ep_model._core,
"core", (2 * LATENT_SIZE,) * 3)
173export_component(ep_model._decoder,
"decoder", (LATENT_SIZE,) * 3)
174export_component(ep_model._output_transform,
"output_transform", (LATENT_SIZE,) * 3)
177parser = ROOT.TMVA.Experimental.SOFIE.RModelParser_ONNX()
178for name
in [
"encoder",
"core",
"decoder",
"output_transform"]:
179 model = parser.Parse(name +
".onnx")
181 model.OutputGenerated()
182 print(
"generated SOFIE model", name +
".hxx")
187fileOut = ROOT.TFile.Open(
"graph_data.root",
"RECREATE")
188tree = ROOT.TTree(
"gdata",
"GNN data")
190node_data = ROOT.std.vector[
"float"]()
191edge_data = ROOT.std.vector[
"float"]()
192global_data = ROOT.std.vector[
"float"]()
193receivers = ROOT.std.vector[
"int"]()
194senders = ROOT.std.vector[
"int"]()
196tree.Branch(
"node_data",
"std::vector<float>", node_data)
197tree.Branch(
"edge_data",
"std::vector<float>", edge_data)
198tree.Branch(
"global_data",
"std::vector<float>", global_data)
199tree.Branch(
"receivers",
"std::vector<int>", receivers)
200tree.Branch(
"senders",
"std::vector<int>", senders)
202print(
"\n\nSaving data in a ROOT File:")
203h1 = ROOT.TH1D(
"h1",
"GNN nodes output", 40, 1, 0)
204h2 = ROOT.TH1D(
"h2",
"GNN edges output", 40, 1, 0)
205h3 = ROOT.TH1D(
"h3",
"GNN global output", 40, 1, 0)
207for i
in range(numevts):
208 graphData = get_dynamic_graph_data_dict(node_size, edge_size, global_size)
209 node_data.assign(graphData[
"nodes"].flatten())
210 edge_data.assign(graphData[
"edges"].flatten())
211 global_data.assign(graphData[
"globals"].flatten())
212 receivers.assign(graphData[
"receivers"].astype(np.int32))
213 senders.assign(graphData[
"senders"].astype(np.int32))
215 dataset.append(graphData)
221for graphData
in dataset:
222 output_gnn = ep_model(
223 torch.from_numpy(graphData[
"nodes"]),
224 torch.from_numpy(graphData[
"edges"]),
225 torch.from_numpy(graphData[
"globals"]),
226 torch.from_numpy(graphData[
"receivers"]),
227 torch.from_numpy(graphData[
"senders"]),
230 h1.Fill(np.mean(output_gnn[-1][0].numpy()))
231 h2.Fill(np.mean(output_gnn[-1][1].numpy()))
232 h3.Fill(np.mean(output_gnn[-1][2].numpy()))
235print(
"time to evaluate ", numevts,
" events", end - start)