Coverage for skema/program_analysis/CAST2FN/ann_cast/to_grfn_pass.py: 16%
356 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-30 17:15 +0000
1import typing
2from functools import singledispatchmethod
4import networkx as nx
5from skema.model_assembly.metadata import LambdaType
6from skema.model_assembly.networks import (
7 GenericNode,
8 GrFNLoopSubgraph,
9 GrFNSubgraph,
10 GroundedFunctionNetwork,
11 HyperEdge,
12 LambdaNode,
13 LoopTopInterface,
14 UnpackNode,
15 PackNode,
16)
17from skema.model_assembly.sandbox import load_lambda_function
18from skema.model_assembly.structures import ContainerIdentifier
19from skema.program_analysis.CAST2FN.ann_cast.ann_cast_helpers import (
20 ELSEBODY,
21 IFBODY,
22 MODULE_SCOPE,
23 GrfnAssignment,
24 call_container_name,
25 con_scope_to_str,
26 create_container_metadata,
27 is_func_def_main,
28 lambda_var_from_fullid,
29)
30from skema.program_analysis.CAST2FN.ann_cast.annotated_cast import *
31from skema.program_analysis.CAST2FN.model.cast import (
32 ScalarType,
33 ValueConstructor,
34)
37class ToGrfnPass:
38 def __init__(self, pipeline_state: PipelineState):
39 self.pipeline_state = pipeline_state
40 self.nodes = self.pipeline_state.nodes
41 self.network = nx.DiGraph()
42 self.subgraphs = nx.DiGraph()
43 self.hyper_edges = []
45 # populate network with variable nodes
46 for grfn_var in self.pipeline_state.grfn_id_to_grfn_var.values():
47 self.network.add_node(grfn_var, **grfn_var.get_kwargs())
49 # the fullid of a AnnCastName node is a string which includes its
50 # variable name, numerical id, version, and scope
51 for node in self.pipeline_state.nodes:
52 self.visit(node, subgraph=None)
54 # build GrFN
55 grfn_uid = GenericNode.create_node_id()
56 timestamp = "timestamp"
57 type_defs = []
58 metadata = []
59 ns = "default-ns"
60 scope = "default"
61 con_name = "GrFN"
62 identifier = ContainerIdentifier(ns, scope, con_name)
64 # store GrFN in PipelineState
65 self.pipeline_state.grfn = GroundedFunctionNetwork(
66 grfn_uid,
67 identifier,
68 timestamp,
69 self.network,
70 self.hyper_edges,
71 self.subgraphs,
72 type_defs,
73 metadata,
74 )
76 def grfn_vars_from_fullids(self, fullids: typing.Iterable):
77 """
78 Return the list of GrFN Variables that are associated with the fullids
79 from `fullids`
80 Paramters:
81 - `fullids`: an iterable of fullids
82 """
83 grfn_vars = []
84 for fullid in fullids:
85 grfn_var = self.pipeline_state.get_grfn_var(fullid)
86 grfn_vars.append(grfn_var)
88 return grfn_vars
90 def create_interface_node(self, lambda_expr):
91 # we should never create an interface node if we have an empty lambda expr
92 assert len(lambda_expr) > 0
93 lambda_uuid = GenericNode.create_node_id()
94 lambda_str = lambda_expr
95 lambda_func = load_lambda_function(lambda_str)
96 # FUTURE: decide on metadata for interface nodes
97 lambda_metadata = []
98 lambda_type = LambdaType.INTERFACE
100 interface_node = LambdaNode(
101 lambda_uuid, lambda_type, lambda_str, lambda_func, lambda_metadata
102 )
104 return interface_node
106 def create_loop_top_interface(self, lambda_expr):
107 # we should never create an interface node if we have an empty lambda expr
108 assert len(lambda_expr) > 0
109 lambda_uuid = GenericNode.create_node_id()
110 lambda_str = lambda_expr
111 lambda_func = load_lambda_function(lambda_str)
112 # FUTURE: decide on metadata for interface nodes
113 lambda_metadata = []
114 lambda_type = LambdaType.LOOP_TOP_INTERFACE
116 interface_node = LoopTopInterface(
117 lambda_uuid, lambda_type, lambda_str, lambda_func, lambda_metadata
118 )
120 return interface_node
122 def add_grfn_edges(
123 self, inputs: typing.List, lambda_node, outputs: typing.List
124 ):
125 """Parameters:
126 - `inputs` and `outputs` are lists of GrFN VariableNode's
127 - `lambda_node` is a GrFN LambdaNode
129 For each input in `inputs`, adds an edge from input to `lambda_node`
130 For each output in `outputs`, adds an edge from `lambda_node` to output
131 Adds a `HyperEdge` between `inputs`, `lambda_node`, and `outputs`
132 """
133 # build input edge set
134 input_edges = zip(inputs, [lambda_node] * len(inputs))
135 # build output edge set
136 output_edges = zip([lambda_node] * len(outputs), outputs)
137 # add edges to network
138 self.network.add_edges_from(input_edges)
139 self.network.add_edges_from(output_edges)
140 # add HyperEdges to GrFN
141 self.hyper_edges.append(HyperEdge(inputs, lambda_node, outputs))
143 def create_condition_node(
144 self, condition_in, condition_out, lambda_expr, subgraph: GrFNSubgraph
145 ):
146 lambda_uuid = GenericNode.create_node_id()
147 lambda_str = lambda_expr
148 lambda_func = load_lambda_function(lambda_str)
149 # FUTURE: decide on metadata for condition nodes
150 lambda_metadata = []
151 lambda_type = LambdaType.CONDITION
153 condition_node = LambdaNode(
154 lambda_uuid, lambda_type, lambda_str, lambda_func, lambda_metadata
155 )
156 self.network.add_node(condition_node, **condition_node.get_kwargs())
158 inputs = self.grfn_vars_from_fullids(condition_in.values())
159 outputs = self.grfn_vars_from_fullids(condition_out.values())
160 self.add_grfn_edges(inputs, condition_node, outputs)
162 # add nodes to subgraph
163 subgraph.nodes.extend(inputs + [condition_node] + outputs)
165 def create_decision_node(
166 self,
167 decision_in,
168 decision_out,
169 condition_var,
170 lambda_expr,
171 subgraph: GrFNSubgraph,
172 ):
173 lambda_uuid = GenericNode.create_node_id()
174 lambda_str = lambda_expr
175 lambda_func = load_lambda_function(lambda_str)
176 # FUTURE: decide on metadata for decision nodes
177 lambda_metadata = []
178 lambda_type = LambdaType.DECISION
180 decision_node = LambdaNode(
181 lambda_uuid, lambda_type, lambda_str, lambda_func, lambda_metadata
182 )
183 self.network.add_node(decision_node, **decision_node.get_kwargs())
185 # FUTURE: modifying the order grfn_vars are added
186 # to inputs may be necessary to perform correct execution
187 # For now, we are following the pattern in `lambda_for_decision()` of
188 # lambda COND, x_if, y_if, x_else, y_else: (x_if, y_if) if COND else (x_else, y_else)
190 # values for decision in are two element dicts with keys IFBODY and ELSEBODY
191 if_body_dict = {}
192 else_body_dict = {}
193 for var_id, fullid in decision_in.items():
194 if_body_dict[var_id] = fullid[IFBODY]
195 else_body_dict[var_id] = fullid[ELSEBODY]
197 if_body_inputs = self.grfn_vars_from_fullids(if_body_dict.values())
198 else_body_inputs = self.grfn_vars_from_fullids(else_body_dict.values())
200 # concatenate if and else inputs after condition_var input to follow pattern
201 inputs = [condition_var] + if_body_inputs + else_body_inputs
202 outputs = self.grfn_vars_from_fullids(decision_out.values())
203 self.add_grfn_edges(inputs, decision_node, outputs)
205 # add nodes to subraph
206 subgraph.nodes.extend(inputs + [decision_node] + outputs)
208 def visit_grfn_assignment(
209 self, grfn_assignment: GrfnAssignment, subgraph: GrFNSubgraph
210 ):
211 assignment_node = grfn_assignment.assignment_node
212 # update func_str and function for assignment node
213 assignment_node.func_str = grfn_assignment.lambda_expr
214 assignment_node.function = load_lambda_function(
215 assignment_node.func_str
216 )
218 self.network.add_node(assignment_node, **assignment_node.get_kwargs())
220 inputs = self.grfn_vars_from_fullids(grfn_assignment.inputs.keys())
221 outputs = self.grfn_vars_from_fullids(grfn_assignment.outputs.keys())
222 self.add_grfn_edges(inputs, assignment_node, outputs)
224 # Create strings representing the inputs and outputs for pack and unpack
225 if isinstance(assignment_node, (PackNode, UnpackNode)):
226 assignment_node.inputs = ",".join(
227 list(
228 map(lambda_var_from_fullid, grfn_assignment.inputs.keys())
229 )
230 )
231 assignment_node.output = ",".join(
232 list(
233 map(lambda_var_from_fullid, grfn_assignment.outputs.keys())
234 )
235 )
237 # add subgraph nodes
238 subgraph.nodes.extend(inputs + [assignment_node] + outputs)
240 def visit(self, node: AnnCastNode, subgraph: GrFNSubgraph):
241 """
242 External visit that callsthe internal visit
243 Useful for debugging/development. For example,
244 printing the nodes that are visited
245 """
246 # print current node being visited.
247 # this can be useful for debugging
248 # class_name = node.__class__.__name__
249 # print(f"\nProcessing node type {class_name}")
251 # call internal visit
252 return self._visit(node, subgraph)
254 def visit_node_list(
255 self, node_list: typing.List[AnnCastNode], subgraph: GrFNSubgraph
256 ):
257 return [self.visit(node, subgraph) for node in node_list]
259 @singledispatchmethod
260 def _visit(self, node: AnnCastNode, subgraph: GrFNSubgraph):
261 """
262 Internal visit
263 """
264 raise NameError(f"Unrecognized node type: {type(node)}")
266 @_visit.register
267 def visit_assignment(
268 self, node: AnnCastAssignment, subgraph: GrFNSubgraph
269 ):
270 self.visit(node.right, subgraph)
271 self.visit_grfn_assignment(node.grfn_assignment, subgraph)
273 @_visit.register
274 def visit_attribute(self, node: AnnCastAttribute, subgraph: GrFNSubgraph):
275 pass
277 @_visit.register
278 def visit_call(self, node: AnnCastCall, subgraph: GrFNSubgraph):
279 if node.is_grfn_2_2:
280 self.visit_call_grfn_2_2(node, subgraph)
281 return
283 self.visit_node_list(node.arguments, subgraph)
284 for index, assignment in node.arg_assignments.items():
285 self.visit_grfn_assignment(assignment, subgraph)
287 parent = subgraph
288 # make a new subgraph for this If Container
289 type = "CallContainer"
290 border_color = GrFNSubgraph.get_border_color(type)
291 metadata = create_container_metadata(node.grfn_con_src_ref)
292 nodes = []
293 parent_str = parent.uid if parent is not None else None
294 occs = 0
295 uid = GenericNode.create_node_id()
296 ns = "default-ns"
297 scope = con_scope_to_str(
298 node.func.con_scope + [call_container_name(node)]
299 )
300 basename = node.func.name # change from 'scope' to its function name
301 basename_id = (
302 node.func.id
303 ) # NOTE: represents the function's name ID, not the call number (i.e. invocation index)
304 subgraph = GrFNSubgraph(
305 uid,
306 ns,
307 scope,
308 basename,
309 basename_id,
310 occs,
311 parent_str,
312 type,
313 border_color,
314 nodes,
315 metadata,
316 )
318 self.subgraphs.add_node(subgraph)
319 self.subgraphs.add_edge(parent, subgraph)
321 # build top interface if needed
322 if len(node.top_interface_in) > 0:
323 top_interface = self.create_interface_node(
324 node.top_interface_lambda
325 )
326 self.network.add_node(top_interface, **top_interface.get_kwargs())
328 inputs = self.grfn_vars_from_fullids(
329 node.top_interface_in.values()
330 )
331 outputs = self.grfn_vars_from_fullids(
332 node.top_interface_out.values()
333 )
334 self.add_grfn_edges(inputs, top_interface, outputs)
336 # container includes top_interface and top_interface outputs
337 subgraph.nodes.extend([top_interface] + outputs)
339 # build bot interface if needed
340 if len(node.bot_interface_in) > 0:
341 bot_interface = self.create_interface_node(
342 node.bot_interface_lambda
343 )
344 self.network.add_node(bot_interface, **bot_interface.get_kwargs())
346 inputs = self.grfn_vars_from_fullids(
347 node.bot_interface_in.values()
348 )
349 outputs = self.grfn_vars_from_fullids(
350 node.bot_interface_out.values()
351 )
352 self.add_grfn_edges(inputs, bot_interface, outputs)
354 # container includes input and bot interface
355 # the outputs need to be added to the parent subgraph
356 subgraph.nodes.extend(inputs + [bot_interface])
357 parent.nodes.extend(outputs)
359 def visit_call_grfn_2_2(self, node: AnnCastCall, subgraph: GrFNSubgraph):
360 # assert isinstance(node.func, AnnCastName)
361 self.visit_node_list(node.arguments, subgraph)
362 for assignment in node.arg_assignments.values():
363 self.visit_grfn_assignment(assignment, subgraph)
365 parent = subgraph
366 # make a new subgraph for this If Container
367 type = "FuncContainer"
368 border_color = GrFNSubgraph.get_border_color(type)
369 metadata = create_container_metadata(
370 node.func_def_copy.grfn_con_src_ref
371 )
372 nodes = []
373 parent_str = parent.uid if parent is not None else None
374 occs = node.invocation_index
375 uid = GenericNode.create_node_id()
376 ns = "default-ns"
377 scope = con_scope_to_str(
378 node.func.con_scope + [call_container_name(node)]
379 )
380 basename = node.func.name
381 basename_id = node.func.id
382 subgraph = GrFNSubgraph(
383 uid,
384 ns,
385 scope,
386 basename,
387 basename_id,
388 occs,
389 parent_str,
390 type,
391 border_color,
392 nodes,
393 metadata,
394 )
395 self.subgraphs.add_node(subgraph)
396 self.subgraphs.add_edge(parent, subgraph)
398 # build top interface
399 if len(node.top_interface_in) > 0:
400 top_interface = self.create_interface_node(
401 node.top_interface_lambda
402 )
403 self.network.add_node(top_interface, **top_interface.get_kwargs())
405 inputs = self.grfn_vars_from_fullids(
406 node.top_interface_in.values()
407 )
408 outputs = self.grfn_vars_from_fullids(
409 node.top_interface_out.values()
410 )
411 self.add_grfn_edges(inputs, top_interface, outputs)
413 # container includes top_interface and top_interface outputs
414 subgraph.nodes.extend([top_interface] + outputs)
416 self.visit_function_def_copy(node.func_def_copy, subgraph)
418 # build bot interface
419 if len(node.bot_interface_in) > 0:
420 bot_interface = self.create_interface_node(
421 node.bot_interface_lambda
422 )
423 self.network.add_node(bot_interface, **bot_interface.get_kwargs())
425 inputs = self.grfn_vars_from_fullids(
426 node.bot_interface_in.values()
427 )
428 outputs = self.grfn_vars_from_fullids(
429 node.bot_interface_out.values()
430 )
431 self.add_grfn_edges(inputs, bot_interface, outputs)
433 # container includes input and bot interface
434 # the outputs need to be added to the parent subgraph
435 subgraph.nodes.extend(inputs + [bot_interface])
436 parent.nodes.extend(outputs)
438 @_visit.register
439 def visit_record_def(self, node: AnnCastRecordDef, subgraph: GrFNSubgraph):
440 pass
442 def visit_function_def_copy(
443 self, node: AnnCastFunctionDef, subgraph: GrFNSubgraph
444 ):
445 for dummy_assignment in node.dummy_grfn_assignments:
446 self.visit_grfn_assignment(dummy_assignment, subgraph)
448 self.visit_node_list(node.func_args, subgraph)
449 self.visit_node_list(node.body, subgraph)
451 @_visit.register
452 def visit_function_def(
453 self, node: AnnCastFunctionDef, subgraph: GrFNSubgraph
454 ):
455 # for GrFN 2.2, we create function containers at call sites,
456 # so we skip all functions except "main"
457 if self.pipeline_state.GENERATE_GRFN_2_2 and not is_func_def_main(
458 node
459 ):
460 return
462 parent = subgraph
463 type = "FuncContainer"
464 border_color = GrFNSubgraph.get_border_color(type)
465 metadata = create_container_metadata(node.grfn_con_src_ref)
466 nodes = []
467 parent_str = parent.uid if parent is not None else None
468 occs = 0
469 uid = GenericNode.create_node_id()
470 ns = "default-ns"
471 scope = con_scope_to_str(node.con_scope)
472 basename = (
473 node.name.name
474 ) # was originally assigned to 'scope', changed to node.name.name
475 basename_id = node.name.id # added
476 subgraph = GrFNSubgraph(
477 uid,
478 ns,
479 scope,
480 basename,
481 basename_id,
482 occs,
483 parent_str,
484 type,
485 border_color,
486 nodes,
487 metadata,
488 )
490 self.visit_node_list(node.func_args, subgraph)
492 # build top interface if needed
493 if len(node.top_interface_in) > 0:
494 top_interface = self.create_interface_node(
495 node.top_interface_lambda
496 )
497 self.network.add_node(top_interface, **top_interface.get_kwargs())
499 inputs = self.grfn_vars_from_fullids(
500 node.top_interface_in.values()
501 )
502 outputs = self.grfn_vars_from_fullids(
503 node.top_interface_out.values()
504 )
505 self.add_grfn_edges(inputs, top_interface, outputs)
507 # add inputs to parent graph
508 parent.nodes.extend(inputs)
509 # add interface node and outputs to subraph
510 subgraph.nodes.extend([top_interface] + outputs)
512 # visit dummy assignments before body
513 for dummy_assignment in node.dummy_grfn_assignments:
514 self.visit_grfn_assignment(dummy_assignment, subgraph)
516 # visit body
517 self.visit_node_list(node.body, subgraph)
519 # build bot interface if needed
520 if len(node.bot_interface_in) > 0:
521 bot_interface = self.create_interface_node(
522 node.bot_interface_lambda
523 )
524 self.network.add_node(bot_interface, **bot_interface.get_kwargs())
526 inputs = self.grfn_vars_from_fullids(
527 node.bot_interface_in.values()
528 )
529 outputs = self.grfn_vars_from_fullids(
530 node.bot_interface_out.values()
531 )
532 self.add_grfn_edges(inputs, bot_interface, outputs)
534 # add interface node and inputs to subraph
535 subgraph.nodes.extend([bot_interface] + inputs)
536 # add outputs to parent graph
537 parent.nodes.extend(outputs)
539 self.subgraphs.add_node(subgraph)
540 self.subgraphs.add_edge(parent, subgraph)
542 @_visit.register
543 def visit_literal_value(
544 self, node: AnnCastLiteralValue, subgraph: GrFNSubgraph
545 ):
546 pass
548 @_visit.register
549 def visit_loop(self, node: AnnCastLoop, subgraph: GrFNSubgraph):
550 if len(node.init) > 0:
551 self.visit_node_list(node.init, subgraph)
553 parent = subgraph
554 # make a new subgraph for this If Container
555 type = "LoopContainer"
556 border_color = GrFNSubgraph.get_border_color(type)
557 metadata = create_container_metadata(node.grfn_con_src_ref)
558 nodes = []
559 parent_str = parent.uid if parent is not None else None
560 occs = 0
561 uid = GenericNode.create_node_id()
562 ns = "default-ns"
563 scope = con_scope_to_str(node.con_scope)
564 basename = "loop" # changed from 'scope' to the string 'loop'
565 basename_id = int(
566 node.con_scope[-1][4:]
567 ) # stripping out the word 'loop' to obtain a numerical ID
568 subgraph = GrFNLoopSubgraph(
569 uid,
570 ns,
571 scope,
572 basename,
573 basename_id,
574 occs,
575 parent_str,
576 type,
577 border_color,
578 nodes,
579 metadata,
580 )
581 self.subgraphs.add_node(subgraph)
582 self.subgraphs.add_edge(parent, subgraph)
584 # build top interface
585 if len(node.top_interface_initial) > 0:
586 top_interface = self.create_loop_top_interface(
587 node.top_interface_lambda
588 )
589 self.network.add_node(top_interface, **top_interface.get_kwargs())
590 # collect initial GrFN VariableNodes
591 grfn_initial = self.grfn_vars_from_fullids(
592 node.top_interface_initial.values()
593 )
594 # collect updated GrFN VariableNodes
595 grfn_updated = self.grfn_vars_from_fullids(
596 node.top_interface_updated.values()
597 )
598 # combine initial and updated for inputs to loop top interface
599 inputs = grfn_initial + grfn_updated
600 # collect ouput GrFN VariableNodes
601 outputs = self.grfn_vars_from_fullids(
602 node.top_interface_out.values()
603 )
604 self.add_grfn_edges(inputs, top_interface, outputs)
606 # add interface node, updated variables, and output variables to subgraph
607 subgraph.nodes.extend([top_interface] + grfn_updated + outputs)
609 # visit expr, then setup condition info
610 self.visit(node.expr, subgraph)
611 self.create_condition_node(
612 node.condition_in,
613 node.condition_out,
614 node.condition_lambda,
615 subgraph,
616 )
618 self.visit_node_list(node.body, subgraph)
620 # build bot interface
621 if len(node.bot_interface_in) > 0:
622 bot_interface = self.create_interface_node(
623 node.bot_interface_lambda
624 )
625 self.network.add_node(bot_interface, **bot_interface.get_kwargs())
627 inputs = self.grfn_vars_from_fullids(
628 node.bot_interface_in.values()
629 )
630 outputs = self.grfn_vars_from_fullids(
631 node.bot_interface_out.values()
632 )
633 self.add_grfn_edges(inputs, bot_interface, outputs)
635 # container includes input and bot interface
636 # the outputs need to be added to the parent subgraph
637 subgraph.nodes.extend(inputs + [bot_interface])
638 parent.nodes.extend(outputs)
640 @_visit.register
641 def visit_model_break(
642 self, node: AnnCastModelBreak, subgraph: GrFNSubgraph
643 ):
644 pass
646 @_visit.register
647 def visit_model_continue(
648 self, node: AnnCastModelContinue, subgraph: GrFNSubgraph
649 ):
650 pass
652 @_visit.register
653 def visit_model_if(self, node: AnnCastModelIf, subgraph: GrFNSubgraph):
654 parent = subgraph
655 # make a new subgraph for this If Container
656 type = "CondContainer"
657 border_color = GrFNSubgraph.get_border_color(type)
658 metadata = create_container_metadata(node.grfn_con_src_ref)
659 nodes = []
660 parent_str = parent.uid if parent is not None else None
661 occs = 0
662 uid = GenericNode.create_node_id()
663 ns = "default-ns"
664 scope = con_scope_to_str(node.con_scope)
665 basename = "if" # changed from 'scope' to the string 'if'
666 basename_id = int(
667 node.con_scope[-1][2:]
668 ) # stripping out the word 'if' to obtain a numerical ID
669 subgraph = GrFNSubgraph(
670 uid,
671 ns,
672 scope,
673 basename,
674 basename_id,
675 occs,
676 parent_str,
677 type,
678 border_color,
679 nodes,
680 metadata,
681 )
682 self.subgraphs.add_node(subgraph)
683 self.subgraphs.add_edge(parent, subgraph)
685 # build top interface
686 if len(node.top_interface_in) > 0:
687 top_interface = self.create_interface_node(
688 node.top_interface_lambda
689 )
690 self.network.add_node(top_interface, **top_interface.get_kwargs())
692 inputs = self.grfn_vars_from_fullids(
693 node.top_interface_in.values()
694 )
695 outputs = self.grfn_vars_from_fullids(
696 node.top_interface_out.values()
697 )
698 self.add_grfn_edges(inputs, top_interface, outputs)
700 # container includes top_interface and top_interface outputs
701 subgraph.nodes.extend([top_interface] + outputs)
703 # visit expr, then setup condition info
704 self.visit(node.expr, subgraph)
705 self.create_condition_node(
706 node.condition_in,
707 node.condition_out,
708 node.condition_lambda,
709 subgraph,
710 )
712 self.visit_node_list(node.body, subgraph)
713 self.visit_node_list(node.orelse, subgraph)
715 condition_var = node.condition_var
716 if len(node.decision_in) > 0:
717 self.create_decision_node(
718 node.decision_in,
719 node.decision_out,
720 condition_var,
721 node.decision_lambda,
722 subgraph,
723 )
725 # build bot interface
726 if len(node.bot_interface_in) > 0:
727 bot_interface = self.create_interface_node(
728 node.bot_interface_lambda
729 )
730 self.network.add_node(bot_interface, **bot_interface.get_kwargs())
732 inputs = self.grfn_vars_from_fullids(
733 node.bot_interface_in.values()
734 )
735 outputs = self.grfn_vars_from_fullids(
736 node.bot_interface_out.values()
737 )
738 self.add_grfn_edges(inputs, bot_interface, outputs)
740 # container includes input and bot interface
741 # the outputs need to be added to the parent subgraph
742 subgraph.nodes.extend(inputs + [bot_interface])
743 parent.nodes.extend(outputs)
745 @_visit.register
746 def visit_model_return(
747 self, node: AnnCastModelReturn, subgraph: GrFNSubgraph
748 ):
749 self.visit(node.value, subgraph)
751 self.visit_grfn_assignment(node.grfn_assignment, subgraph)
753 @_visit.register
754 def visit_module(self, node: AnnCastModule, subgraph: GrFNSubgraph):
755 type = "ModuleContainer"
756 border_color = GrFNSubgraph.get_border_color(type)
757 metadata = create_container_metadata(node.grfn_con_src_ref)
758 nodes = []
759 occs = 0
760 parent_str = None
761 uid = GenericNode.create_node_id()
762 ns = "default-ns"
763 scope = MODULE_SCOPE
764 basename = MODULE_SCOPE
765 basename_id = -1
766 subgraph = GrFNSubgraph(
767 uid,
768 ns,
769 scope,
770 basename,
771 basename_id,
772 occs,
773 parent_str,
774 type,
775 border_color,
776 nodes,
777 metadata,
778 )
779 self.subgraphs.add_node(subgraph)
781 self.visit_node_list(node.body, subgraph)
783 @_visit.register
784 def visit_name(self, node: AnnCastName, subgraph: GrFNSubgraph):
785 pass
787 @_visit.register
788 def visit_set(self, node: AnnCastSet, subgraph: GrFNSubgraph):
789 pass
791 @_visit.register
792 def visit_tuple(self, node: AnnCastTuple, subgraph: GrFNSubgraph):
793 self.visit_node_list(node.values, subgraph)
795 @_visit.register
796 def visit_var(self, node: AnnCastVar, subgraph: GrFNSubgraph):
797 self.visit(node.val, subgraph)