diff --git a/pyre/graphs/graphStrongComponent0.html b/pyre/graphs/graphStrongComponent0.html
new file mode 100644
index 0000000..c56c684
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent0.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent0.json b/pyre/graphs/graphStrongComponent0.json
new file mode 100644
index 0000000..3f5f6b0
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent0.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame", "label": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame", "shape": "dot"}], "edges": [{"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames_to_sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_transitive_trace_frames"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_annotations", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_annotation_trace"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_raw_trace_frame"}, {"id": "sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames_to_sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame", "from": "sapp.sapp.pipeline.model_generator.ModelGenerator._get_or_populate_trace_frames", "to": "sapp.sapp.pipeline.model_generator.ModelGenerator._generate_trace_frame"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent1.html b/pyre/graphs/graphStrongComponent1.html
new file mode 100644
index 0000000..9580cf2
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent1.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent1.json b/pyre/graphs/graphStrongComponent1.json
new file mode 100644
index 0000000..6b572f6
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent1.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive", "label": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive", "shape": "dot"}, {"color": "#97c2fc", "id": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_object", "label": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_object", "shape": "dot"}, {"color": "#97c2fc", "id": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_array", "label": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_array", "shape": "dot"}, {"color": "#97c2fc", "id": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_template", "label": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_template", "shape": "dot"}], "edges": [{"id": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive_to_watchman.watchman.python.pywatchman.pybser.Bunser.unser_object", "from": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive", "to": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_object"}, {"id": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive_to_watchman.watchman.python.pywatchman.pybser.Bunser.unser_array", "from": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive", "to": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_array"}, {"id": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive_to_watchman.watchman.python.pywatchman.pybser.Bunser.unser_template", "from": "watchman.watchman.python.pywatchman.pybser.Bunser.loads_recursive", "to": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_template"}, {"id": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_template_to_watchman.watchman.python.pywatchman.pybser.Bunser.unser_array", "from": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_template", "to": "watchman.watchman.python.pywatchman.pybser.Bunser.unser_array"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent2.html b/pyre/graphs/graphStrongComponent2.html
new file mode 100644
index 0000000..9955cab
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent2.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent2.json b/pyre/graphs/graphStrongComponent2.json
new file mode 100644
index 0000000..0413a31
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent2.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace", "label": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame", "label": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame", "shape": "dot"}, {"color": "#97c2fc", "id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation", "label": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation", "shape": "dot"}], "edges": [{"id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace_to_sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame", "from": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace", "to": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame"}, {"id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation_to_sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace", "from": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation", "to": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._populate_trace"}, {"id": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame_to_sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation", "from": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_frame", "to": "sapp.sapp.trimmed_trace_graph.TrimmedTraceGraph._add_trace_annotation"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent3.html b/pyre/graphs/graphStrongComponent3.html
new file mode 100644
index 0000000..3f4121d
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent3.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent3.json b/pyre/graphs/graphStrongComponent3.json
new file mode 100644
index 0000000..2f04fa7
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent3.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat", "label": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat", "shape": "dot"}, {"color": "#97c2fc", "id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat", "label": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat", "shape": "dot"}, {"color": "#97c2fc", "id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages", "label": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages", "shape": "dot"}], "edges": [{"id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat_to_autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat", "from": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat", "to": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat"}, {"id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages_to_autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat", "from": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages", "to": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.add_chat"}, {"id": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat_to_autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages", "from": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.clear_chat", "to": "autogpts.ZEROAGPT_03.forge.agent.ForgeAgent.set_instruction_messages"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent4.html b/pyre/graphs/graphStrongComponent4.html
new file mode 100644
index 0000000..746facd
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent4.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent4.json b/pyre/graphs/graphStrongComponent4.json
new file mode 100644
index 0000000..2ca04a3
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent4.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict", "label": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict", "shape": "dot"}, {"color": "#97c2fc", "id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.parse_properties", "label": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.parse_properties", "shape": "dot"}], "edges": [{"id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict_to_autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.parse_properties", "from": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict", "to": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.parse_properties"}, {"id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict_to_autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict", "from": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict", "to": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.from_dict"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent5.html b/pyre/graphs/graphStrongComponent5.html
new file mode 100644
index 0000000..1d32651
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent5.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent5.json b/pyre/graphs/graphStrongComponent5.json
new file mode 100644
index 0000000..9c83647
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent5.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.to_typescript_object_interface", "label": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.to_typescript_object_interface", "shape": "dot"}, {"color": "#97c2fc", "id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type", "label": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type", "shape": "dot"}], "edges": [{"id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.to_typescript_object_interface_to_autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type", "from": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.to_typescript_object_interface", "to": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type"}, {"id": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type_to_autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type", "from": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type", "to": "autogpts.autogpt.autogpt.core.utils.json_schema.JSONSchema.typescript_type"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent6.html b/pyre/graphs/graphStrongComponent6.html
new file mode 100644
index 0000000..064191d
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent6.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent6.json b/pyre/graphs/graphStrongComponent6.json
new file mode 100644
index 0000000..a1f1c25
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent6.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader.get_project_hash", "label": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader.get_project_hash", "shape": "dot"}, {"color": "#97c2fc", "id": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader._compute_project_hash", "label": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader._compute_project_hash", "shape": "dot"}], "edges": [{"id": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader.get_project_hash_to_watchman.build.fbcode_builder.getdeps.load.ManifestLoader._compute_project_hash", "from": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader.get_project_hash", "to": "watchman.build.fbcode_builder.getdeps.load.ManifestLoader._compute_project_hash"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent7.html b/pyre/graphs/graphStrongComponent7.html
new file mode 100644
index 0000000..66888a7
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent7.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent7.json b/pyre/graphs/graphStrongComponent7.json
new file mode 100644
index 0000000..a6d3a95
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent7.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "beebot.body.body.Body.save", "label": "beebot.body.body.Body.save", "shape": "dot"}, {"color": "#97c2fc", "id": "beebot.body.body.Body.setup", "label": "beebot.body.body.Body.setup", "shape": "dot"}], "edges": [{"id": "beebot.body.body.Body.save_to_beebot.body.body.Body.setup", "from": "beebot.body.body.Body.save", "to": "beebot.body.body.Body.setup"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent8.html b/pyre/graphs/graphStrongComponent8.html
new file mode 100644
index 0000000..f9c2fb2
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent8.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent8.json b/pyre/graphs/graphStrongComponent8.json
new file mode 100644
index 0000000..7a122aa
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent8.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "lollms.terminal.MainMenu.install_model", "label": "lollms.terminal.MainMenu.install_model", "shape": "dot"}, {"color": "#97c2fc", "id": "lollms.terminal.MainMenu.select_model", "label": "lollms.terminal.MainMenu.select_model", "shape": "dot"}], "edges": [{"id": "lollms.terminal.MainMenu.install_model_to_lollms.terminal.MainMenu.select_model", "from": "lollms.terminal.MainMenu.install_model", "to": "lollms.terminal.MainMenu.select_model"}, {"id": "lollms.terminal.MainMenu.select_model_to_lollms.terminal.MainMenu.select_model", "from": "lollms.terminal.MainMenu.select_model", "to": "lollms.terminal.MainMenu.select_model"}]}
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent9.html b/pyre/graphs/graphStrongComponent9.html
new file mode 100644
index 0000000..5cf1906
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent9.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pyre/graphs/graphStrongComponent9.json b/pyre/graphs/graphStrongComponent9.json
new file mode 100644
index 0000000..69b8258
--- /dev/null
+++ b/pyre/graphs/graphStrongComponent9.json
@@ -0,0 +1 @@
+{"nodes": [{"color": "#97c2fc", "id": "build.lib.lollms.terminal.MainMenu.select_model", "label": "build.lib.lollms.terminal.MainMenu.select_model", "shape": "dot"}, {"color": "#97c2fc", "id": "build.lib.lollms.terminal.MainMenu.install_model", "label": "build.lib.lollms.terminal.MainMenu.install_model", "shape": "dot"}], "edges": [{"id": "build.lib.lollms.terminal.MainMenu.select_model_to_build.lib.lollms.terminal.MainMenu.select_model", "from": "build.lib.lollms.terminal.MainMenu.select_model", "to": "build.lib.lollms.terminal.MainMenu.select_model"}, {"id": "build.lib.lollms.terminal.MainMenu.select_model_to_build.lib.lollms.terminal.MainMenu.install_model", "from": "build.lib.lollms.terminal.MainMenu.select_model", "to": "build.lib.lollms.terminal.MainMenu.install_model"}]}
\ No newline at end of file
diff --git a/pyre/strong_graphs.py b/pyre/strong_graphs.py
new file mode 100644
index 0000000..a03fa4e
--- /dev/null
+++ b/pyre/strong_graphs.py
@@ -0,0 +1,284 @@
+import json
+import math
+from pathlib import Path
+from typing import Any, Dict, List, Tuple
+import csv
+import matplotlib.patches as patches
+import matplotlib.pyplot as plt
+import networkx as nx
+import numpy as np
+from pyvis.network import Network
+import click
+import json
+import networkx
+
+def bezier_curve(
+ src: np.ndarray, ctrl: List[float], dst: np.ndarray
+) -> List[np.ndarray]:
+ """
+ Generate Bézier curve points.
+
+ Args:
+ - src (np.ndarray): The source point.
+ - ctrl (List[float]): The control point.
+ - dst (np.ndarray): The destination point.
+
+ Returns:
+ - List[np.ndarray]: The Bézier curve points.
+ """
+ curve = []
+ for t in np.linspace(0, 1, num=100):
+ curve_point = (
+ np.outer((1 - t) ** 2, src)
+ + 2 * np.outer((1 - t) * t, ctrl)
+ + np.outer(t**2, dst)
+ )
+ curve.append(curve_point[0])
+ return curve
+
+
+def curved_edges(
+ G: nx.Graph, pos: Dict[Any, Tuple[float, float]], dist: float = 0.2
+) -> None:
+ """
+ Draw curved edges for nodes on the same level.
+
+ Args:
+ - G (Any): The graph object.
+ - pos (Dict[Any, Tuple[float, float]]): Dictionary with node positions.
+ - dist (float, optional): Distance for curvature. Defaults to 0.2.
+
+ Returns:
+ - None
+ """
+ ax = plt.gca()
+ for u, v, data in G.edges(data=True):
+ src = np.array(pos[u])
+ dst = np.array(pos[v])
+
+ same_level = abs(src[1] - dst[1]) < 0.01
+
+ if same_level:
+ control = [(src[0] + dst[0]) / 2, src[1] + dist]
+ curve = bezier_curve(src, control, dst)
+ arrow = patches.FancyArrowPatch(
+ posA=curve[0], # type: ignore
+ posB=curve[-1], # type: ignore
+ connectionstyle=f"arc3,rad=0.2",
+ color="gray",
+ arrowstyle="-|>",
+ mutation_scale=15.0,
+ lw=1,
+ shrinkA=10,
+ shrinkB=10,
+ )
+ ax.add_patch(arrow)
+ else:
+ ax.annotate(
+ "",
+ xy=dst,
+ xytext=src,
+ arrowprops=dict(
+ arrowstyle="-|>", color="gray", lw=1, shrinkA=10, shrinkB=10
+ ),
+ )
+
+
+def tree_layout(graph: nx.DiGraph, root_node: Any) -> Dict[Any, Tuple[float, float]]:
+ """Compute positions as a tree layout centered on the root with alternating vertical shifts."""
+ bfs_tree = nx.bfs_tree(graph, source=root_node)
+ levels = {
+ node: depth
+ for node, depth in nx.single_source_shortest_path_length(
+ bfs_tree, root_node
+ ).items()
+ }
+
+ pos = {}
+ max_depth = max(levels.values())
+ level_positions = {i: 0 for i in range(max_depth + 1)} # type: ignore
+
+ # Count the number of nodes per level to compute the width
+ level_count: Any = {}
+ for node, level in levels.items():
+ level_count[level] = level_count.get(level, 0) + 1
+
+ vertical_offset = (
+ 0.07 # The amount of vertical shift per node within the same level
+ )
+
+ # Assign positions
+ for node, level in sorted(levels.items(), key=lambda x: x[1]):
+ total_nodes_in_level = level_count[level]
+ horizontal_spacing = 1.0 / (total_nodes_in_level + 1)
+ pos_x = (
+ 0.5
+ - (total_nodes_in_level - 1) * horizontal_spacing / 2
+ + level_positions[level] * horizontal_spacing
+ )
+
+ # Alternately shift nodes up and down within the same level
+ pos_y = (
+ -level
+ + (level_positions[level] % 2) * vertical_offset
+ - ((level_positions[level] + 1) % 2) * vertical_offset
+ )
+ pos[node] = (pos_x, pos_y)
+
+ level_positions[level] += 1
+
+ return pos
+
+
+def graph_spring_layout(
+ dag: nx.DiGraph, labels: Dict[Any, str], tree: bool = True
+) -> None:
+ num_nodes = len(dag.nodes())
+ # Setting up the figure and axis
+ fig, ax = plt.subplots()
+ ax.axis("off") # Turn off the axis
+
+ base = 3.0
+
+ if num_nodes > 10:
+ base /= 1 + math.log(num_nodes)
+ font_size = base * 10
+
+ font_size = max(10, base * 10)
+ node_size = max(300, base * 1000)
+
+ if tree:
+ root_node = [node for node, degree in dag.in_degree() if degree == 0][0]
+ pos = tree_layout(dag, root_node)
+ else:
+ # Adjust k for the spring layout based on node count
+ k_value = 3 / math.sqrt(num_nodes)
+
+ pos = nx.spring_layout(dag, k=k_value, iterations=50)
+
+ # Draw nodes and labels
+ nx.draw_networkx_nodes(dag, pos, node_color="skyblue", node_size=int(node_size))
+ nx.draw_networkx_labels(dag, pos, labels=labels, font_size=int(font_size))
+
+ # Draw curved edges
+ curved_edges(dag, pos) # type: ignore
+
+ plt.tight_layout()
+ plt.show()
+
+
+def rgb_to_hex(rgb: Tuple[float, float, float]) -> str:
+ return "#{:02x}{:02x}{:02x}".format(
+ int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)
+ )
+
+
+def get_category_colors(categories: Dict[Any, str]) -> Dict[str, str]:
+ unique_categories = set(categories.values())
+ colormap = plt.cm.get_cmap("tab10", len(unique_categories)) # type: ignore
+ return {
+ category: rgb_to_hex(colormap(i)[:3])
+ for i, category in enumerate(unique_categories)
+ }
+
+
+def graph_interactive_network(
+ dag: nx.DiGraph,
+ labels: Dict[Any, Dict[str, Any]],
+ html_graph_path: str = "",
+) -> None:
+
+ # now lets split
+ # nx.
+ #largest_components = sorted(nx.connected_components(dag), key=len, reverse=True)[:n]
+ size = 10
+ #dag = dag.to_undirected()
+ largest_components = sorted(nx.strongly_connected_components(dag), key=len, reverse=True)[:size]
+ #largest_components = sorted(nx.connected_components(dag), key=len, reverse=True)[:size]
+ for index in range(size):
+ name= f'StrongComponent{index}'
+ component=dag.subgraph(largest_components[index])
+ nt = Network(notebook=True, width="100%", height="800px",
+ #directed=True
+ )
+ for edge in component.edges():
+ source_id_str = edge[0]
+ target_id_str = edge[1]
+ edge_id_str = (
+ f"{source_id_str}_to_{target_id_str}" # Construct a unique edge id
+ )
+ nt.add_node(source_id_str)
+ nt.add_node(target_id_str)
+ nt.add_edge(source_id_str, target_id_str, id=edge_id_str)
+ hierarchical_options = {
+ "enabled": True,
+ #"levelSeparation": 200, # Increased vertical spacing between levels
+ #"nodeSpacing": 250, # Increased spacing between nodes on the same level
+ #"treeSpacing": 250, # Increased spacing between different trees (for forest)
+ "blockShifting": True,
+ # "edgeMinimization": True,
+ "parentCentralization": True,
+ #"direction": "UD",
+ "sortMethod": "directed",
+ }
+ physics_options = {
+ "stabilization": {
+ "enabled": True,
+ "iterations": 1000, # Default is often around 100
+ },
+ "hierarchicalRepulsion": {
+ "centralGravity": 0.0,
+ "springLength": 700, # Increased edge length
+ "springConstant": 0.01,
+ "nodeDistance": 750, # Increased minimum distance between nodes
+ "damping": 0.09,
+ },
+ "solver": "hierarchicalRepulsion",
+ "timestep": 0.5,
+ }
+ nt.options = {
+ "nodes": {
+ "font": {
+ "size": 12, # Increased font size for labels
+ "color": "black", # Set a readable font color
+ },
+ "shapeProperties": {"useBorderWithImage": True},
+ },
+ "edges": {
+ "length": 1050, # Increased edge length
+ },
+ "physics": physics_options,
+ "layout": {"hierarchical": hierarchical_options},
+ }
+ graph_data = {"nodes": nt.nodes, "edges": nt.edges}
+ json_graph = json.dumps(graph_data)
+ with open(f"graphs/graph{name}.json", "w") as f:
+ f.write(json_graph)
+ nt.show(f"graphs/graph{name}.html")
+
+
+@click.command()
+@click.argument('infile', type=click.File('r'))
+def main(infile):
+ ind = csv.DictReader(infile)
+ dag = networkx.DiGraph()
+ for r in ind:
+ #(caller,callee) =
+ parts = r["name"].split("-")
+ caller = parts[0]
+ callee = parts[1]
+ count = int(r["count"])
+ #print(caller, callee, count)
+ dag.add_edge(caller,callee, weight=count)
+ #with input ",name,count"
+
+ #dag.add_nodes_from(self.items)
+ # for item in self.items:
+ # nodeid = clean_nodeid(item.nodeid)
+ # for dependency in self.dependencies[nodeid].dependencies:
+ # dag.add_edge(self.nodeid_to_item[dependency], item)
+
+ labels = {}
+ graph_interactive_network(dag, labels, html_graph_path="")
+if __name__ == "__main__":
+ main()