shukdevdatta123's picture
Update app.py
1997d9e verified
raw
history blame
87.1 kB
import streamlit as st #
import itertools as it
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from operator import itemgetter
import math # Import the math module
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
from streamlit.components.v1 import html
import matplotlib.colors as mpl
# Sidebar for selecting an option
sidebar_option = st.sidebar.radio("Select an option",
["Introductory Tutorial", "Basic: Properties",
"Basic: Read and write graphs", "Basic: Simple graph",
"Basic: Simple graph Directed", "Drawing: Custom Node Position",
"Drawing: Cluster Layout", "Drawing: Degree Analysis",
"Drawing: Ego Graph", "Drawing: Eigenvalues", "Drawing: Four Grids",
"Drawing: House With Colors", "Drawing: Labels And Colors",
"Drawing: Multipartite Layout", "Drawing: Node Colormap",
"Drawing: Rainbow Coloring", "Drawing: Random Geometric Graph","Drawing: Self-loops",
"Drawing: Simple Path", "Drawing: Spectral Embedding", "Drawing: Traveling Salesman Problem",
"Drawing: Weighted Graph", "3D Drawing: Animations of 3D Rotation", "3D Drawing: Basic Matplotlib",
"Graph: DAG - Topological Layout", "Graph: Erdos Renyi", "Graph: Karate Club", "Graph: Minimum Spanning Tree",
"Graph: Triads", "Algorithms: Cycle Detection", "Algorithms: Greedy Coloring"])
# Display content when "Select an option" is chosen
if sidebar_option == "Introductory Tutorial":
st.title("Graph Visualization and Analysis Options")
# Add content descriptions
descriptions = [
("Basic: Properties", "This option provides insights into the foundational aspects of a graph. You can count nodes (individual points) and edges (connections between nodes). For example, in a graph representing a social network, the nodes could be people, and the edges could represent friendships. The degree distribution tells how many connections each node has, helping identify influential nodes."),
("Basic: Read and Write Graphs", "This feature lets you load graphs from files or save your current graph for later use. For instance, if you have a graph stored in a GML file, you can load it and analyze it in your program. Similarly, you can save graphs as adjacency lists or edge lists for portability."),
("Basic: Simple Graph", "This generates simple, undirected graphs where edges don’t have a direction. For example, a graph showing roads between cities where travel is possible in both directions. You can create specific structures like a star graph (one central hub) or a cycle graph (nodes connected in a loop)."),
("Basic: Simple Graph Directed", "Directed graphs have edges with a direction. They are useful for workflows or dependencies. For example, in a project plan, a directed graph might show tasks with arrows indicating the order in which they need to be completed."),
("Drawing: Custom Node Position", "This feature allows you to manually set where each node appears on the graph. For example, in a family tree, you might want to position nodes to reflect generational hierarchies rather than relying on an automatic layout."),
("Drawing: Cluster Layout", "Nodes are grouped into clusters based on their connections. For instance, in a network of social media users, this could highlight friend groups. Each group would appear as a tight cluster in the visualization."),
("Drawing: Degree Analysis", "This visualizes the connections (or degree) of nodes. For example, in a transportation network, hubs like airports can be highlighted because they have the highest degree, representing more connections to other nodes."),
("Drawing: Ego Graph", "Focuses on a single node and its immediate connections. For instance, if you want to see all direct friends of a specific person on a social network, this feature isolates that person and their relationships."),
("Drawing: Eigenvalues", "Eigenvalues come from the graph’s Laplacian matrix and reveal structural properties. For example, in community detection, eigenvalues can help identify clusters or measure the connectivity of a graph."),
("Drawing: House With Colors", "Displays a basic house graph, a simple structure that resembles a house. For example, you could use it for teaching graph theory basics, with color-coded nodes and edges representing different parts of the structure."),
("Drawing: Labels and Colors", "This lets you customize the appearance of nodes and edges by adding labels or colors. For example, in a roadmap, cities (nodes) can be color-coded by region, and roads (edges) can have labels for distance."),
("Drawing: Multipartite Layout", "Creates multipartite graphs where nodes are divided into layers, and edges only connect nodes from different layers. For instance, in a university, one layer could represent professors and another students, with edges indicating which professor teaches which student."),
("Drawing: Node Colormap", "Applies color gradients to nodes based on their properties, like degree or centrality. For example, nodes in a social network can be shaded to show influence, with darker colors for highly connected individuals."),
("Drawing: Rainbow Coloring", "This colorful feature assigns different colors to edges, helping differentiate them. For example, in a circular graph, this can show the relative positions of connections, making it visually appealing."),
("Drawing: Random Geometric Graph", "Generates graphs where nodes are connected if they’re within a specific distance. For example, in a wireless sensor network, nodes represent sensors, and edges show connectivity based on signal range."),
("Drawing: Self-loops", "Visualizes edges that start and end at the same node. For example, in a citation network, a self-loop could represent a researcher citing their previous work."),
("Drawing: Simple Path", "Displays simple linear graphs where nodes connect in a sequence. For example, it could represent a production line where each step depends on the previous one."),
("Drawing: Spectral Embedding", "Uses a mathematical technique to arrange nodes in a lower-dimensional space. For example, you can visualize clusters in a high-dimensional dataset in a way that preserves their relationships."),
("Drawing: Traveling Salesman Problem", "Visualizes solutions to the Traveling Salesman Problem (TSP), where the goal is to find the shortest route visiting every node once. For example, a delivery route optimization can use this to minimize travel costs."),
("Drawing: Weighted Graph", "Shows graphs with weighted edges. For example, in a flight network, edge weights can represent ticket prices or distances, with thicker edges for higher weights."),
("3D Drawing: Animations of 3D Rotation", "Generates 3D graphs with rotation animations. For example, you can visualize molecule structures or spatial relationships dynamically."),
("3D Drawing: Basic Matplotlib", "Creates 3D graph visualizations using Matplotlib, letting you explore spatial relationships. For example, you could map a city’s buildings in 3D space."),
("Graph: DAG - Topological Layout", "Displays Directed Acyclic Graphs (DAGs) in a topological order. For example, it can represent workflows or dependency graphs where tasks need to follow a sequence."),
("Graph: Erdos Renyi", "Generates random graphs where edges appear based on a probability. For example, you can model random connections in a network to study statistical properties."),
("Graph: Karate Club", "This graph is a classic benchmark in network science, showing relationships in a club. It’s often used for community detection and teaching graph analysis."),
("Graph: Minimum Spanning Tree", "Extracts a tree from the graph connecting all nodes with the minimum total edge weight. For example, this is used in network design to minimize cable or pipeline costs."),
("Graph: Triads", "Analyzes three-node structures (triads). For example, in social networks, closed triads (triangles) indicate strong relationships among three people."),
("Algorithms: Cycle Detection", "Detects cycles in graphs, useful for spotting feedback loops or circular dependencies. For example, in a dependency graph, it can help identify tasks that reference each other."),
("Algorithms: Greedy Coloring", "Colors nodes so that no two adjacent nodes share the same color. For example, in exam scheduling, this ensures no two overlapping exams are assigned the same room.")
]
for title, desc in descriptions:
st.subheader(title) # Removed the ### here
st.write(desc)
st.write("---")
def plot_greedy_coloring(graph):
# Apply greedy coloring
graph_coloring = nx.greedy_color(graph)
unique_colors = set(graph_coloring.values())
# Assign colors to nodes based on the greedy coloring
graph_color_to_mpl_color = dict(zip(unique_colors, mpl.TABLEAU_COLORS))
node_colors = [graph_color_to_mpl_color[graph_coloring[n]] for n in graph.nodes()]
# Layout of the graph
pos = nx.spring_layout(graph, seed=14)
# Draw the graph
nx.draw(
graph,
pos,
with_labels=True,
node_size=500,
node_color=node_colors,
edge_color="grey",
font_size=12,
font_color="#333333",
width=2,
)
plt.title("Greedy Coloring of Graph")
st.pyplot(plt)
def algorithms_greedy_coloring():
st.title("Algorithms: Greedy Coloring")
# Option to choose between creating your own or using the default example
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="The default example shows a predefined graph, or you can create your own."
)
if graph_mode == "Default Example":
# Create a predefined graph (Dodecahedral graph) for the greedy coloring example
G = nx.dodecahedral_graph()
st.write("Default Graph: Dodecahedral Graph with Greedy Coloring.")
plot_greedy_coloring(G)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Graph")
# Input for creating a custom graph
nodes_input = st.text_area("Enter nodes (e.g., 1, 2, 3, 4):")
edges_input = st.text_area("Enter edges (e.g., (1, 2), (2, 3), (3, 4)):").strip()
if st.button("Generate Graph"):
if nodes_input and edges_input:
try:
# Clean and parse the input for nodes (strip spaces, remove empty strings)
nodes = [node.strip() for node in nodes_input.split(",") if node.strip()]
nodes = list(map(int, nodes))
# Clean and parse the input for edges (strip spaces and remove empty strings)
edges = [edge.strip() for edge in edges_input.split("),") if edge.strip()]
edges = [tuple(map(int, edge.strip("()").split(","))) for edge in edges]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
st.write("Custom Graph:", G.edges())
plot_greedy_coloring(G)
except Exception as e:
st.error(f"Error creating the graph: {e}")
else:
st.error("Please enter valid nodes and edges.")
if sidebar_option == "Algorithms: Greedy Coloring":
algorithms_greedy_coloring()
# Helper function to draw and display graph
def draw_graph(G, pos=None, title="Graph Visualization"):
plt.figure(figsize=(8, 6))
nx.draw(G, pos=pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10, font_weight='bold')
st.pyplot(plt)
def plot_cycle_detection(graph):
# Draw the graph
pos = nx.spring_layout(graph, seed=8020)
nx.draw(graph, pos, with_labels=True, node_size=2000, node_color="lightblue")
# Find all cycles in the directed graph
try:
cycles = list(nx.simple_cycles(graph))
if cycles:
st.write("Cycles Detected:")
for cycle in cycles:
st.write(cycle)
# Highlight each cycle in red
for cycle in cycles:
edges_in_cycle = [(cycle[i], cycle[i + 1] if i + 1 < len(cycle) else cycle[0]) for i in range(len(cycle))]
nx.draw_networkx_edges(graph, pos, edgelist=edges_in_cycle, edge_color="r", width=2)
else:
st.write("No cycles detected")
except Exception as e:
st.error(f"Error detecting cycles: {e}")
# Display the graph
plt.title("Cycle Detection in Directed Graph")
st.pyplot(plt)
def algorithms_cycle_detection():
st.title("Algorithms: Cycle Detection")
# Option to choose between creating your own or using the default example
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="The default example shows a predefined graph, or you can create your own."
)
if graph_mode == "Default Example":
# Create a predefined graph with multiple cycles
G = nx.DiGraph([(1, 2), (2, 3), (3, 4), (4, 2)])
st.write("Default Graph: A simple directed graph with multiple cycles.")
plot_cycle_detection(G)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Graph")
# Input for creating custom graph
edges_input = st.text_area("Enter directed edges (e.g., (1, 2), (2, 3), (3, 1), (3, 4)):").strip()
if st.button("Generate Graph"):
if edges_input:
try:
edges = []
# Ensure correct formatting of the input string
edge_strings = edges_input.split("),")
for edge_str in edge_strings:
edge_str = edge_str.strip()
if edge_str:
# Handle the case where the edge might be missing a closing parenthesis
if edge_str[-1] != ")":
edge_str += ")"
# Remove the opening and closing parentheses
edge_tuple = edge_str.strip("()").split(",")
if len(edge_tuple) == 2:
try:
# Safely convert to integers and add the edge
edge_tuple = tuple(map(int, edge_tuple))
edges.append(edge_tuple)
except ValueError:
st.error(f"Invalid edge format: {edge_str}")
return
if edges:
# Create the graph
G = nx.DiGraph(edges)
st.write("Custom Graph:", G.edges())
plot_cycle_detection(G)
else:
st.error("No valid edges provided.")
except Exception as e:
st.error(f"Error creating the graph: {e}")
else:
st.error("Please enter valid directed edges.")
# Display the corresponding page based on sidebar option
if sidebar_option == "Algorithms: Cycle Detection":
algorithms_cycle_detection()
def triads_graph():
st.title("Graph: Triads")
# Sidebar selection for Default Example or Custom Triads
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows predefined triads, or you can create your own triads."
)
if graph_mode == "Default Example":
# Define the triads
triads = {
"003": [],
"012": [(1, 2)],
"102": [(1, 2), (2, 1)],
"021D": [(3, 1), (3, 2)],
"021U": [(1, 3), (2, 3)],
"021C": [(1, 3), (3, 2)],
"111D": [(1, 2), (2, 1), (3, 1)],
"111U": [(1, 2), (2, 1), (1, 3)],
"030T": [(1, 2), (3, 2), (1, 3)],
"030C": [(1, 3), (3, 2), (2, 1)],
"201": [(1, 2), (2, 1), (3, 1), (1, 3)],
"120D": [(1, 2), (2, 1), (3, 1), (3, 2)],
"120U": [(1, 2), (2, 1), (1, 3), (2, 3)],
"120C": [(1, 2), (2, 1), (1, 3), (3, 2)],
"210": [(1, 2), (2, 1), (1, 3), (3, 2), (2, 3)],
"300": [(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)],
}
fig, axes = plt.subplots(4, 4, figsize=(10, 10))
for (title, triad), ax in zip(triads.items(), axes.flatten()):
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3])
G.add_edges_from(triad)
nx.draw_networkx(
G,
ax=ax,
with_labels=True, # Labels the vertices
node_color=["green"],
node_size=200,
arrowsize=20,
width=2,
pos=nx.planar_layout(G),
)
ax.set_xlim(val * 1.2 for val in ax.get_xlim())
ax.set_ylim(val * 1.2 for val in ax.get_ylim())
ax.text(
0,
0,
title,
fontsize=15,
fontweight="extra bold",
horizontalalignment="center",
bbox={"boxstyle": "square,pad=0.3", "fc": "none"},
)
fig.tight_layout()
st.pyplot(fig)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Triads")
# Input: Enter triads as a dictionary (e.g., {'triad_name': [(1, 2), (2, 1)]})
triad_input = st.text_area(
"Enter your triads in the format: {'triad_name': [(edge1), (edge2), ...]}",
value="{'003': [], '012': [(1, 2)]}"
)
# Generate Button
if st.button("Generate Graph"):
# Try to evaluate the input as a dictionary of triads
try:
custom_triads = eval(triad_input)
if isinstance(custom_triads, dict) and all(isinstance(value, list) and all(isinstance(edge, tuple) and len(edge) == 2 for edge in value) for value in custom_triads.values()):
fig, axes = plt.subplots(len(custom_triads), 1, figsize=(10, len(custom_triads) * 5))
if len(custom_triads) == 1: # Handle case where only one triad is entered
axes = [axes]
for (title, triad), ax in zip(custom_triads.items(), axes):
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3])
G.add_edges_from(triad)
nx.draw_networkx(
G,
ax=ax,
with_labels=True, # Labels the vertices
node_color=["green"],
node_size=200,
arrowsize=20,
width=2,
pos=nx.planar_layout(G),
)
ax.set_xlim(val * 1.2 for val in ax.get_xlim())
ax.set_ylim(val * 1.2 for val in ax.get_ylim())
ax.text(
0,
0,
title,
fontsize=15,
fontweight="extra bold",
horizontalalignment="center",
bbox={"boxstyle": "square,pad=0.3", "fc": "none"},
)
fig.tight_layout()
st.pyplot(fig)
else:
st.error("Invalid format. Please enter a dictionary of triads in the format {'triad_name': [(edge1), (edge2), ...]}.")
except Exception as e:
st.error(f"Error parsing input: {e}")
# Display the corresponding page based on sidebar option
if sidebar_option == "Graph: Triads":
triads_graph()
def minimum_spanning_tree_graph():
st.title("Graph: Minimum Spanning Tree")
# Sidebar selection for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows a graph and its minimum spanning tree, or you can create your own graph."
)
if graph_mode == "Default Example":
# Create a default graph
G = nx.Graph()
G.add_edges_from(
[
(0, 1, {"weight": 4}),
(0, 7, {"weight": 8}),
(1, 7, {"weight": 11}),
(1, 2, {"weight": 8}),
(2, 8, {"weight": 2}),
(2, 5, {"weight": 4}),
(2, 3, {"weight": 7}),
(3, 4, {"weight": 9}),
(3, 5, {"weight": 14}),
(4, 5, {"weight": 10}),
(5, 6, {"weight": 2}),
(6, 8, {"weight": 6}),
(7, 8, {"weight": 7}),
]
)
# Find the minimum spanning tree
T = nx.minimum_spanning_tree(G)
# Visualize the graph and the minimum spanning tree
pos = nx.spring_layout(G)
fig, ax = plt.subplots(figsize=(8, 8))
nx.draw_networkx_nodes(G, pos, node_color="lightblue", node_size=500, ax=ax)
nx.draw_networkx_edges(G, pos, edge_color="grey", ax=ax)
nx.draw_networkx_labels(G, pos, font_size=12, font_family="sans-serif", ax=ax)
nx.draw_networkx_edge_labels(
G, pos, edge_labels={(u, v): d["weight"] for u, v, d in G.edges(data=True)}, ax=ax
)
nx.draw_networkx_edges(T, pos, edge_color="green", width=2, ax=ax)
ax.set_title("Graph and Minimum Spanning Tree")
plt.axis("off")
st.pyplot(fig)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Graph")
# Allow user to input the number of nodes and edges for custom graph
num_nodes = st.number_input("Number of nodes", min_value=2, value=5)
num_edges = st.number_input("Number of edges", min_value=1, value=6)
# Create empty graph
G = nx.Graph()
# Allow user to input the edges and their weights manually
edges = []
for i in range(num_edges):
source = st.number_input(f"Source node for edge {i+1}", min_value=0, max_value=num_nodes-1, key=f"source_{i}")
dest = st.number_input(f"Destination node for edge {i+1}", min_value=0, max_value=num_nodes-1, key=f"dest_{i}")
weight = st.number_input(f"Weight for edge ({source}, {dest})", min_value=1, value=1, key=f"weight_{i}")
edges.append((source, dest, {"weight": weight}))
# Add edges to the graph
G.add_edges_from(edges)
# Add nodes to the graph (to ensure all nodes are included, even if not explicitly added by the user)
G.add_nodes_from(range(num_nodes))
# Button to generate the graph and calculate MST
if st.button("Generate Graph"):
# Find the minimum spanning tree
T = nx.minimum_spanning_tree(G)
# Visualize the graph and the minimum spanning tree
pos = nx.spring_layout(G)
fig, ax = plt.subplots(figsize=(8, 8))
nx.draw_networkx_nodes(G, pos, node_color="lightblue", node_size=500, ax=ax)
nx.draw_networkx_edges(G, pos, edge_color="grey", ax=ax)
nx.draw_networkx_labels(G, pos, font_size=12, font_family="sans-serif", ax=ax)
nx.draw_networkx_edge_labels(
G, pos, edge_labels={(u, v): d["weight"] for u, v, d in G.edges(data=True)}, ax=ax
)
nx.draw_networkx_edges(T, pos, edge_color="green", width=2, ax=ax)
ax.set_title("Custom Graph and Minimum Spanning Tree")
plt.axis("off")
st.pyplot(fig)
# Display the corresponding page based on sidebar option
if sidebar_option == "Graph: Minimum Spanning Tree":
minimum_spanning_tree_graph()
def karate_club_graph():
st.title("Graph: Karate Club")
# Sidebar selection for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows the Karate Club graph, or you can create your own graph."
)
if graph_mode == "Default Example":
# Load the Karate Club graph
G = nx.karate_club_graph()
# Display node degree
st.write("### Node Degree")
for v in G:
st.write(f"Node {v:4}: Degree = {G.degree(v)}")
# Visualize the graph using circular layout
st.write("### Graph Visualization")
fig, ax = plt.subplots()
nx.draw_circular(G, with_labels=True, ax=ax, node_color="skyblue", edge_color="gray")
ax.set_title("Karate Club Graph")
st.pyplot(fig)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Graph")
# Allow user to input the number of nodes and edges for custom graph
num_nodes = st.number_input("Number of nodes", min_value=2, value=10)
num_edges = st.number_input("Number of edges", min_value=1, value=15)
seed = st.number_input("Seed for Random Graph (optional)", value=20160)
# Generate graph button
if st.button("Generate Graph"):
# Create random graph with user input
G = nx.gnm_random_graph(num_nodes, num_edges, seed=seed)
# Display node degree
st.write("### Node Degree")
for v in G:
st.write(f"Node {v:4}: Degree = {G.degree(v)}")
# Visualize the graph using circular layout
st.write("### Graph Visualization")
fig, ax = plt.subplots()
nx.draw_circular(G, with_labels=True, ax=ax, node_color="lightgreen", edge_color="gray")
ax.set_title("Custom Graph")
st.pyplot(fig)
# Display the corresponding page based on sidebar option
if sidebar_option == "Graph: Karate Club":
karate_club_graph()
def erdos_renyi_graph():
st.title("Graph: Erdos Renyi")
# Sidebar selection for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows a random graph, or you can create your own Erdos-Renyi graph."
)
if graph_mode == "Default Example":
# Default random graph parameters
n = 10 # 10 nodes
m = 20 # 20 edges
seed = 20160 # seed random number generators for reproducibility
# Create a button for generating the graph
if st.button("Generate Graph"):
# Create random graph
G = nx.gnm_random_graph(n, m, seed=seed)
# Display node properties
st.write("### Node Degree and Clustering Coefficient")
for v in nx.nodes(G):
st.write(f"Node {v}: Degree = {nx.degree(G, v)}, Clustering Coefficient = {nx.clustering(G, v)}")
# Display adjacency list
st.write("### Adjacency List")
adj_list = "\n".join([line for line in nx.generate_adjlist(G)])
st.text(adj_list)
# Visualize the graph
pos = nx.spring_layout(G, seed=seed) # Seed for reproducible layout
fig, ax = plt.subplots()
nx.draw(G, pos=pos, ax=ax, with_labels=True, node_color="skyblue", edge_color="gray")
ax.set_title("Erdos-Renyi Random Graph")
st.pyplot(fig)
elif graph_mode == "Create Your Own":
st.write("### Create Your Own Random Erdos-Renyi Graph")
# Allow user to input the number of nodes and edges
n = st.number_input("Number of nodes (n)", min_value=2, value=10)
m = st.number_input("Number of edges (m)", min_value=1, value=20)
seed = st.number_input("Seed", value=20160)
# Create a button for generating the graph
if st.button("Generate Graph"):
# Create random graph
G = nx.gnm_random_graph(n, m, seed=seed)
# Display node properties
st.write("### Node Degree and Clustering Coefficient")
for v in nx.nodes(G):
st.write(f"Node {v}: Degree = {nx.degree(G, v)}, Clustering Coefficient = {nx.clustering(G, v)}")
# Display adjacency list
st.write("### Adjacency List")
adj_list = "\n".join([line for line in nx.generate_adjlist(G)])
st.text(adj_list)
# Visualize the graph
pos = nx.spring_layout(G, seed=seed) # Seed for reproducible layout
fig, ax = plt.subplots()
nx.draw(G, pos=pos, ax=ax, with_labels=True, node_color="skyblue", edge_color="gray")
ax.set_title("Erdos-Renyi Random Graph")
st.pyplot(fig)
# Display the corresponding page based on sidebar option
if sidebar_option == "Graph: Erdos Renyi":
erdos_renyi_graph()
def dag_topological_layout():
st.title("Graph: DAG - Topological Layout")
# Sidebar selection for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows DAG layout in topological order, or you can create your own DAG."
)
if graph_mode == "Default Example":
# Default DAG example
G = nx.DiGraph(
[
("f", "a"),
("a", "b"),
("a", "e"),
("b", "c"),
("b", "d"),
("d", "e"),
("f", "c"),
("f", "g"),
("h", "f"),
]
)
# Add layer attribute for multipartite_layout
for layer, nodes in enumerate(nx.topological_generations(G)):
for node in nodes:
G.nodes[node]["layer"] = layer
# Compute the multipartite_layout using the "layer" node attribute
pos = nx.multipartite_layout(G, subset_key="layer")
# Draw the graph
fig, ax = plt.subplots()
nx.draw_networkx(G, pos=pos, ax=ax)
ax.set_title("DAG layout in topological order")
fig.tight_layout()
st.pyplot(fig)
elif graph_mode == "Create Your Own":
st.write("### Custom DAG Creation")
# Allow the user to input the number of nodes
num_nodes = st.number_input("Enter the number of nodes", min_value=2, value=5)
# Create node names based on the number of nodes
nodes = [str(i) for i in range(num_nodes)]
st.write(f"### Nodes: {nodes}")
st.write("#### Add Edges between Nodes")
# Allow the user to select pairs of nodes to add edges
edges = []
for i in range(num_nodes):
for j in range(i + 1, num_nodes):
edge = (nodes[i], nodes[j])
if st.checkbox(f"Add edge from {edge[0]} to {edge[1]}", value=False):
edges.append(edge)
# Create the custom DAG
G_custom = nx.DiGraph()
G_custom.add_edges_from(edges)
# Add layer attribute for multipartite_layout
for layer, nodes in enumerate(nx.topological_generations(G_custom)):
for node in nodes:
G_custom.nodes[node]["layer"] = layer
# Compute the multipartite_layout using the "layer" node attribute
pos_custom = nx.multipartite_layout(G_custom, subset_key="layer")
# Draw the custom DAG
fig_custom, ax_custom = plt.subplots()
nx.draw_networkx(G_custom, pos=pos_custom, ax=ax_custom)
ax_custom.set_title("Custom DAG layout in topological order")
fig_custom.tight_layout()
st.pyplot(fig_custom)
# Display the corresponding page based on sidebar option
if sidebar_option == "Graph: DAG - Topological Layout":
dag_topological_layout()
if sidebar_option == "3D Drawing: Animations of 3D Rotation":
st.title("3D Drawing: Animations of 3D Rotation")
# Provide options for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows a dodecahedral graph, or you can create your own custom graph."
)
# Define the function to create animation
def generate_animation(G, pos, frames=100):
nodes = np.array([pos[v] for v in G])
edges = np.array([(pos[u], pos[v]) for u, v in G.edges()])
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
def init():
ax.scatter(*nodes.T, alpha=0.2, s=100, color="blue")
for vizedge in edges:
ax.plot(*vizedge.T, color="gray")
ax.grid(False)
ax.set_axis_off()
plt.tight_layout()
return
def _frame_update(index):
ax.view_init(index * 0.2, index * 0.5)
return
ani = animation.FuncAnimation(
fig,
_frame_update,
init_func=init,
interval=50,
cache_frame_data=False,
frames=frames,
)
return ani
# Default Example
if graph_mode == "Default Example":
G = nx.dodecahedral_graph()
pos = nx.spectral_layout(G, dim=3)
ani = generate_animation(G, pos)
# Create Your Own Example
else:
st.write("### Customize Your Graph")
num_nodes = st.slider("Number of Nodes", min_value=5, max_value=50, value=20)
edge_prob = st.slider("Edge Probability", min_value=0.1, max_value=1.0, value=0.3)
# Generate custom graph
G = nx.erdos_renyi_graph(num_nodes, edge_prob)
pos = nx.spectral_layout(G, dim=3)
ani = generate_animation(G, pos)
# Display animation in Streamlit
with st.spinner("Rendering animation..."):
ani.save("animation.gif", writer="imagemagick")
st.image("animation.gif", caption="3D Graph Rotation", use_container_width=True)
# Default example code
def default_example():
G = nx.cycle_graph(20)
# 3d spring layout
pos = nx.spring_layout(G, dim=3, seed=779)
# Extract node and edge positions from the layout
node_xyz = np.array([pos[v] for v in sorted(G)])
edge_xyz = np.array([(pos[u], pos[v]) for u, v in G.edges()])
# Create the 3D figure
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
# Plot the nodes - alpha is scaled by "depth" automatically
ax.scatter(*node_xyz.T, s=100, ec="w")
# Plot the edges
for vizedge in edge_xyz:
ax.plot(*vizedge.T, color="tab:gray")
def _format_axes(ax):
"""Visualization options for the 3D axes."""
# Turn gridlines off
ax.grid(False)
# Suppress tick labels
for dim in (ax.xaxis, ax.yaxis, ax.zaxis):
dim.set_ticks([])
# Set axes labels
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
_format_axes(ax)
fig.tight_layout()
st.pyplot(fig)
# Create your own graph option
def create_own_graph():
# Input fields to customize the graph
nodes = st.number_input("Number of nodes", min_value=2, max_value=50, value=20)
seed = st.number_input("Seed for layout", value=779)
# Add a button to generate the graph
generate_button = st.button("Generate Graph")
if generate_button:
# Generate graph and layout
G = nx.cycle_graph(nodes)
pos = nx.spring_layout(G, dim=3, seed=seed)
# Extract node and edge positions
node_xyz = np.array([pos[v] for v in sorted(G)])
edge_xyz = np.array([(pos[u], pos[v]) for u, v in G.edges()])
# Create the 3D figure
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
# Plot the nodes
ax.scatter(*node_xyz.T, s=100, ec="w")
# Plot the edges
for vizedge in edge_xyz:
ax.plot(*vizedge.T, color="tab:gray")
def _format_axes(ax):
"""Visualization options for the 3D axes."""
ax.grid(False)
for dim in (ax.xaxis, ax.yaxis, ax.zaxis):
dim.set_ticks([])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
_format_axes(ax)
fig.tight_layout()
st.pyplot(fig)
if sidebar_option == "3D Drawing: Basic Matplotlib":
st.title("3D Drawing: Basic Matplotlib")
# Provide options for Default Example or Custom Graph
graph_mode = st.radio(
"Choose a Mode:",
("Default Example", "Create Your Own"),
help="Default example shows a cycle graph, or you can create your own custom graph."
)
# Display the chosen option
if graph_mode == "Default Example":
default_example()
elif graph_mode == "Create Your Own":
create_own_graph()
# Function to display Weighted Graph
def display_weighted_graph():
st.title("Drawing: Weighted Graph")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default weighted graph example
G = nx.Graph()
G.add_edge("a", "b", weight=0.6)
G.add_edge("a", "c", weight=0.2)
G.add_edge("c", "d", weight=0.1)
G.add_edge("c", "e", weight=0.7)
G.add_edge("c", "f", weight=0.9)
G.add_edge("a", "d", weight=0.3)
elarge = [(u, v) for (u, v, d) in G.edges(data=True) if d["weight"] > 0.5]
esmall = [(u, v) for (u, v, d) in G.edges(data=True) if d["weight"] <= 0.5]
pos = nx.spring_layout(G, seed=7) # positions for all nodes - seed for reproducibility
# nodes
nx.draw_networkx_nodes(G, pos, node_size=700)
# edges
nx.draw_networkx_edges(G, pos, edgelist=elarge, width=6)
nx.draw_networkx_edges(
G, pos, edgelist=esmall, width=6, alpha=0.5, edge_color="b", style="dashed"
)
# node labels
nx.draw_networkx_labels(G, pos, font_size=20, font_family="sans-serif")
# edge weight labels
edge_labels = nx.get_edge_attributes(G, "weight")
nx.draw_networkx_edge_labels(G, pos, edge_labels)
ax = plt.gca()
ax.margins(0.08)
plt.axis("off")
plt.tight_layout()
st.pyplot(plt)
elif option == "Create your own":
# User can create their own graph with edges and weights
edge_input = st.text_area(
"Enter edges with weights (format: node1,node2,weight;node1,node2,weight;...)",
"a,b,0.6;a,c,0.2;c,d,0.1;c,e,0.7;c,f,0.9;a,d,0.3"
)
# Parse the input string
edges = edge_input.split(";")
edge_list = []
for edge in edges:
node1, node2, weight = edge.split(",")
edge_list.append((node1.strip(), node2.strip(), float(weight.strip())))
# Add a button to generate the graph
generate_button = st.button("Generate Graph")
if generate_button:
G_custom = nx.Graph()
# Add edges to the graph
for node1, node2, weight in edge_list:
G_custom.add_edge(node1, node2, weight=weight)
# Create layout for visualization
pos = nx.spring_layout(G_custom, seed=7)
# Determine edges based on weight
elarge = [(u, v) for (u, v, d) in G_custom.edges(data=True) if d["weight"] > 0.5]
esmall = [(u, v) for (u, v, d) in G_custom.edges(data=True) if d["weight"] <= 0.5]
# Draw the graph
nx.draw_networkx_nodes(G_custom, pos, node_size=700)
nx.draw_networkx_edges(G_custom, pos, edgelist=elarge, width=6)
nx.draw_networkx_edges(
G_custom, pos, edgelist=esmall, width=6, alpha=0.5, edge_color="b", style="dashed"
)
nx.draw_networkx_labels(G_custom, pos, font_size=20, font_family="sans-serif")
edge_labels = nx.get_edge_attributes(G_custom, "weight")
nx.draw_networkx_edge_labels(G_custom, pos, edge_labels)
ax = plt.gca()
ax.margins(0.08)
plt.axis("off")
plt.tight_layout()
st.pyplot(plt)
# Display Drawing: Weighted Graph if selected
if sidebar_option == "Drawing: Weighted Graph":
display_weighted_graph()
from networkx.algorithms.approximation import christofides
# Function to display Traveling Salesman Problem
def display_tsp():
st.title("Drawing: Traveling Salesman Problem")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default example of random geometric graph with TSP solution
G = nx.random_geometric_graph(20, radius=0.4, seed=3)
pos = nx.get_node_attributes(G, "pos")
# Depot should be at (0.5, 0.5)
pos[0] = (0.5, 0.5)
H = G.copy()
# Calculating the distances between the nodes as edge's weight.
for i in range(len(pos)):
for j in range(i + 1, len(pos)):
dist = math.hypot(pos[i][0] - pos[j][0], pos[i][1] - pos[j][1])
dist = dist
G.add_edge(i, j, weight=dist)
# Find TSP cycle using Christofides' approximation
cycle = christofides(G, weight="weight")
edge_list = list(nx.utils.pairwise(cycle))
# Draw closest edges on each node only
nx.draw_networkx_edges(H, pos, edge_color="blue", width=0.5)
# Draw the route
nx.draw_networkx(
G,
pos,
with_labels=True,
edgelist=edge_list,
edge_color="red",
node_size=200,
width=3,
)
st.pyplot(plt)
st.write("The route of the traveler is:", cycle)
elif option == "Create your own":
# User can create their own graph
num_nodes = st.slider("Number of nodes:", min_value=3, max_value=30, value=20)
radius = st.slider("Edge radius:", min_value=0.1, max_value=1.0, value=0.4)
# Add a button to generate a new graph
generate_button = st.button("Generate Graph")
if generate_button:
# Create random geometric graph based on user input
G_custom = nx.random_geometric_graph(num_nodes, radius, seed=3)
pos = nx.get_node_attributes(G_custom, "pos")
# Depot should be at (0.5, 0.5)
pos[0] = (0.5, 0.5)
H = G_custom.copy()
# Calculating the distances between the nodes as edge's weight.
for i in range(len(pos)):
for j in range(i + 1, len(pos)):
dist = math.hypot(pos[i][0] - pos[j][0], pos[i][1] - pos[j][1])
dist = dist
G_custom.add_edge(i, j, weight=dist)
# Find TSP cycle using Christofides' approximation
cycle = christofides(G_custom, weight="weight")
edge_list = list(nx.utils.pairwise(cycle))
# Draw closest edges on each node only
nx.draw_networkx_edges(H, pos, edge_color="blue", width=0.5)
# Draw the TSP route
nx.draw_networkx(
G_custom,
pos,
with_labels=True,
edgelist=edge_list,
edge_color="red",
node_size=200,
width=3,
)
st.pyplot(plt)
st.write("The route of the traveler is:", cycle)
# Display Drawing: Traveling Salesman Problem if selected
if sidebar_option == "Drawing: Traveling Salesman Problem":
display_tsp()
# Function to display Drawing: Spectral Embedding
def display_spectral_embedding():
st.title("Drawing: Spectral Embedding")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default example of spectral embedding with a grid graph
options = {"node_color": "C0", "node_size": 100} # No labels
G = nx.grid_2d_graph(6, 6)
fig, axs = plt.subplots(3, 3, figsize=(12, 12))
axs = axs.flatten()
for i in range(7): # Looping over 7 images
if i == 0:
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 1:
G.remove_edge((2, 2), (2, 3))
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 2:
G.remove_edge((3, 2), (3, 3))
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 3:
G.remove_edge((2, 2), (3, 2))
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 4:
G.remove_edge((2, 3), (3, 3))
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 5:
G.remove_edge((1, 2), (1, 3))
nx.draw_spectral(G, **options, ax=axs[i])
elif i == 6:
G.remove_edge((4, 2), (4, 3))
nx.draw_spectral(G, **options, ax=axs[i])
# Hide the last two subplots (8th and 9th)
for j in range(7, 9):
fig.delaxes(axs[j]) # Delete the extra axes
st.pyplot(fig)
elif option == "Create your own":
# User can interactively modify the grid and see the results
grid_size = st.slider("Choose grid size (n x n):", min_value=3, max_value=10, value=6)
G_custom = nx.grid_2d_graph(grid_size, grid_size)
# List all edges to allow removal
all_edges = list(G_custom.edges())
# Collect user input for edges to remove (before showing the "Generate" button)
selected_edges_per_graph = []
for i in range(7): # Loop over 7 graphs
selected_edges = st.multiselect(f"Select edges to remove for graph {i+1}:",
options=[str(edge) for edge in all_edges])
selected_edges_per_graph.append(selected_edges)
# Add "Generate" button after edge selection
generate_button = st.button("Generate Graph")
if generate_button:
fig, axs = plt.subplots(3, 3, figsize=(12, 12))
axs = axs.flatten()
# Loop through each subplot and allow edge removal individually
for i in range(7): # Loop over 7 graphs
edges_to_remove = [tuple(eval(edge)) for edge in selected_edges_per_graph[i]]
# Remove the selected edges
G_custom_copy = G_custom.copy()
G_custom_copy.remove_edges_from(edges_to_remove)
# Draw the graph with removed edges
nx.draw_spectral(G_custom_copy, **{"node_color": "C0", "node_size": 100}, ax=axs[i])
# Hide the last two subplots (8th and 9th)
for j in range(7, 9):
fig.delaxes(axs[j]) # Delete the extra axes
st.pyplot(fig)
# Display Drawing: Spectral Embedding if selected
if sidebar_option == "Drawing: Spectral Embedding":
display_spectral_embedding()
# Function to display Drawing: Simple Path
def display_simple_path():
st.title("Drawing: Simple Path")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default example of a simple path graph
G = nx.path_graph(8)
pos = nx.spring_layout(G, seed=47) # Seed layout for reproducibility
# Draw the graph
nx.draw(G, pos=pos)
st.pyplot(plt)
elif option == "Create your own":
# User can create their own path graph with a custom number of nodes
num_nodes = st.number_input("Number of nodes in the path:", min_value=2, max_value=50, value=8)
if st.button("Generate Graph"):
# Generate a path graph with user-specified number of nodes
G_custom = nx.path_graph(num_nodes)
pos = nx.spring_layout(G_custom, seed=47) # Seed layout for reproducibility
# Draw the graph
nx.draw(G_custom, pos=pos)
st.pyplot(plt)
# Display Drawing: Simple Path if selected
if sidebar_option == "Drawing: Simple Path":
display_simple_path()
# Function to display Drawing: Self-loops
def display_self_loops():
st.title("Drawing: Self-loops")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default example of a graph with self-loops
G = nx.complete_graph(3, create_using=nx.DiGraph)
G.add_edge(0, 0) # Add a self-loop to node 0
pos = nx.circular_layout(G)
# Draw the graph
nx.draw(G, pos, with_labels=True)
# Add self-loops to the remaining nodes
edgelist = [(1, 1), (2, 2)]
G.add_edges_from(edgelist)
# Draw the newly added self-loops with different formatting
nx.draw_networkx_edges(G, pos, edgelist=edgelist, arrowstyle="<|-", style="dashed")
st.pyplot(plt)
elif option == "Create your own":
# User can create their own graph with self-loops
num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=20, value=3)
add_self_loops = st.checkbox("Add self-loops to all nodes?", value=True)
if st.button("Generate Graph"):
# Generate a complete graph
G = nx.complete_graph(num_nodes, create_using=nx.DiGraph)
# Optionally add self-loops to all nodes
if add_self_loops:
for node in G.nodes():
G.add_edge(node, node)
pos = nx.circular_layout(G)
# Draw the graph with self-loops
nx.draw(G, pos, with_labels=True)
# Style self-loops differently
edgelist = [(node, node) for node in G.nodes()]
nx.draw_networkx_edges(G, pos, edgelist=edgelist, arrowstyle="<|-", style="dashed")
st.pyplot(plt)
# Display Drawing: Self-loops if selected
if sidebar_option == "Drawing: Self-loops":
display_self_loops()
# Function to display Drawing: Random Geometric Graph
def display_random_geometric_graph():
st.title("Drawing: Random Geometric Graph")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Default random geometric graph example
G = nx.random_geometric_graph(200, 0.125, seed=896803)
pos = nx.get_node_attributes(G, "pos")
# Find node near the center (0.5, 0.5)
dmin = 1
ncenter = 0
for n in pos:
x, y = pos[n]
d = (x - 0.5) ** 2 + (y - 0.5) ** 2
if d < dmin:
ncenter = n
dmin = d
# Color by path length from node near center
p = dict(nx.single_source_shortest_path_length(G, ncenter))
plt.figure(figsize=(8, 8))
nx.draw_networkx_edges(G, pos, alpha=0.4)
nx.draw_networkx_nodes(
G,
pos,
nodelist=list(p.keys()),
node_size=80,
node_color=list(p.values()),
cmap=plt.cm.Reds_r,
)
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)
plt.axis("off")
st.pyplot(plt)
elif option == "Create your own":
# User can create their own random geometric graph
num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=500, value=200)
distance = st.slider("Edge distance threshold (between 0 and 1):", 0.01, 1.0, 0.125)
if st.button("Generate Graph"):
# Generate the graph with user input
G = nx.random_geometric_graph(num_nodes, distance, seed=896803)
pos = nx.get_node_attributes(G, "pos")
# Find node near the center (0.5, 0.5)
dmin = 1
ncenter = 0
for n in pos:
x, y = pos[n]
d = (x - 0.5) ** 2 + (y - 0.5) ** 2
if d < dmin:
ncenter = n
dmin = d
# Color by path length from node near center
p = dict(nx.single_source_shortest_path_length(G, ncenter))
plt.figure(figsize=(8, 8))
nx.draw_networkx_edges(G, pos, alpha=0.4)
nx.draw_networkx_nodes(
G,
pos,
nodelist=list(p.keys()),
node_size=80,
node_color=list(p.values()),
cmap=plt.cm.Reds_r,
)
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)
plt.axis("off")
st.pyplot(plt)
# Display Drawing: Random Geometric Graph if selected
if sidebar_option == "Drawing: Random Geometric Graph":
display_random_geometric_graph()
# Function to display Drawing: Rainbow Coloring
def display_rainbow_coloring():
st.title("Drawing: Rainbow Coloring")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Rainbow Coloring with default parameters
node_dist_to_color = {
1: "tab:red",
2: "tab:orange",
3: "tab:olive",
4: "tab:green",
5: "tab:blue",
6: "tab:purple",
}
nnodes = 13
G = nx.complete_graph(nnodes)
n = (nnodes - 1) // 2
ndist_iter = list(range(1, n + 1))
ndist_iter += ndist_iter[::-1]
def cycle(nlist, n):
return nlist[-n:] + nlist[:-n]
nodes = list(G.nodes())
for i, nd in enumerate(ndist_iter):
for u, v in zip(nodes, cycle(nodes, i + 1)):
G[u][v]["color"] = node_dist_to_color[nd]
pos = nx.circular_layout(G)
# Create a figure with 1:1 aspect ratio to preserve the circle.
fig, ax = plt.subplots(figsize=(8, 8))
node_opts = {"node_size": 500, "node_color": "w", "edgecolors": "k", "linewidths": 2.0}
nx.draw_networkx_nodes(G, pos, **node_opts)
nx.draw_networkx_labels(G, pos, font_size=14)
# Extract color from edge data
edge_colors = [edgedata["color"] for _, _, edgedata in G.edges(data=True)]
nx.draw_networkx_edges(G, pos, width=2.0, edge_color=edge_colors)
ax.set_axis_off()
fig.tight_layout()
st.pyplot(plt)
elif option == "Create your own":
nnodes = st.number_input("Number of nodes (max=14):", min_value=2, max_value=50, value=13)
# Allow users to create their own color map
red = st.color_picker("Select a color for Red (1)", "#ff0000")
orange = st.color_picker("Select a color for Orange (2)", "#ff7f00")
olive = st.color_picker("Select a color for Olive (3)", "#808000")
green = st.color_picker("Select a color for Green (4)", "#008000")
blue = st.color_picker("Select a color for Blue (5)", "#0000ff")
purple = st.color_picker("Select a color for Purple (6)", "#800080")
node_dist_to_color = {
1: red,
2: orange,
3: olive,
4: green,
5: blue,
6: purple,
}
if st.button("Generate Graph"):
G = nx.complete_graph(nnodes)
n = (nnodes - 1) // 2
ndist_iter = list(range(1, n + 1))
ndist_iter += ndist_iter[::-1]
def cycle(nlist, n):
return nlist[-n:] + nlist[:-n]
nodes = list(G.nodes())
for i, nd in enumerate(ndist_iter):
for u, v in zip(nodes, cycle(nodes, i + 1)):
G[u][v]["color"] = node_dist_to_color[nd]
pos = nx.circular_layout(G)
# Create a figure with 1:1 aspect ratio to preserve the circle.
fig, ax = plt.subplots(figsize=(8, 8))
node_opts = {"node_size": 500, "node_color": "w", "edgecolors": "k", "linewidths": 2.0}
nx.draw_networkx_nodes(G, pos, **node_opts)
nx.draw_networkx_labels(G, pos, font_size=14)
# Extract color from edge data
edge_colors = [edgedata["color"] for _, _, edgedata in G.edges(data=True)]
nx.draw_networkx_edges(G, pos, width=2.0, edge_color=edge_colors)
ax.set_axis_off()
fig.tight_layout()
st.pyplot(plt)
# Display Drawing: Rainbow Coloring if selected
if sidebar_option == "Drawing: Rainbow Coloring":
display_rainbow_coloring()
# Function to display Drawing: Node Colormap
def display_node_colormap():
st.title("Drawing: Node Colormap")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.cycle_graph(24)
pos = nx.circular_layout(G)
nx.draw(G, pos, node_color=range(24), node_size=800, cmap=plt.cm.Blues)
st.pyplot(plt)
elif option == "Create your own":
num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=100, value=24)
color_map = st.selectbox("Select a colormap:", plt.colormaps(), index=plt.colormaps().index('Blues'))
if st.button("Generate Graph"):
# Create cycle graph with custom number of nodes
G_custom = nx.cycle_graph(num_nodes)
pos = nx.circular_layout(G_custom)
nx.draw(G_custom, pos, node_color=range(num_nodes), node_size=800, cmap=plt.get_cmap(color_map))
st.pyplot(plt)
# Display Drawing: Node Colormap if selected
if sidebar_option == "Drawing: Node Colormap":
display_node_colormap()
# Function to create a multipartite graph
def multilayered_graph(*subset_sizes):
G = nx.Graph()
layers = len(subset_sizes)
node_id = 0
# Create nodes for each subset and add edges between nodes in adjacent layers
for i, size in enumerate(subset_sizes):
for j in range(size):
G.add_node(node_id, layer=i) # Assign a layer attribute
node_id += 1
# Add edges between nodes in adjacent layers
node_ids = list(G.nodes())
for i in range(layers - 1):
layer_nodes = [node for node in node_ids if G.nodes[node]["layer"] == i]
next_layer_nodes = [node for node in node_ids if G.nodes[node]["layer"] == i + 1]
for node in layer_nodes:
for next_node in next_layer_nodes:
G.add_edge(node, next_node)
return G
# Function to display Multipartite Layout
def display_multipartite_layout():
st.title("Drawing: Multipartite Layout")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
subset_sizes = [5, 5, 4, 3, 2, 4, 4, 3]
subset_color = [
"gold", "violet", "violet", "violet", "violet",
"limegreen", "limegreen", "darkorange"
]
# Generate and plot multipartite graph
G = multilayered_graph(*subset_sizes)
color = [subset_color[data["layer"]] for v, data in G.nodes(data=True)]
pos = nx.multipartite_layout(G, subset_key="layer")
plt.figure(figsize=(8, 8))
nx.draw(G, pos, node_color=color, with_labels=False)
plt.axis("equal")
st.pyplot(plt)
elif option == "Create your own":
# Let the user input the subset sizes and colors
st.write("Enter the subset sizes and colors to create your own multipartite graph.")
subset_sizes_input = st.text_area("Enter subset sizes (comma-separated, e.g., 5,5,4,3):", value="5,5,4,3,2,4,4,3")
subset_sizes = list(map(int, subset_sizes_input.split(',')))
subset_colors_input = st.text_area("Enter subset colors (comma-separated, e.g., gold,violet,green):", value="gold,violet,violet,violet,violet,limegreen,limegreen,darkorange")
subset_colors = subset_colors_input.split(',')
# Check if the number of colors matches the number of subsets
if len(subset_sizes) != len(subset_colors):
st.error("The number of colors should match the number of subsets.")
else:
# Add a button to generate the graph
if st.button("Generate Graph"):
# Generate and plot multipartite graph
G = multilayered_graph(*subset_sizes)
color = [subset_colors[data["layer"]] for v, data in G.nodes(data=True)]
pos = nx.multipartite_layout(G, subset_key="layer")
plt.figure(figsize=(8, 8))
nx.draw(G, pos, node_color=color, with_labels=False)
plt.axis("equal")
st.pyplot(plt)
# Display Drawing: Multipartite Layout if selected
if sidebar_option == "Drawing: Multipartite Layout":
display_multipartite_layout()
# Function to display Labels and Colors
def display_labels_and_colors():
st.title("Drawing: Labels And Colors")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Create a cubical graph
G = nx.cubical_graph()
pos = nx.spring_layout(G, seed=3113794652) # positions for all nodes
# Draw nodes with different colors
options = {"edgecolors": "tab:gray", "node_size": 800, "alpha": 0.9}
nx.draw_networkx_nodes(G, pos, nodelist=[0, 1, 2, 3], node_color="tab:red", **options)
nx.draw_networkx_nodes(G, pos, nodelist=[4, 5, 6, 7], node_color="tab:blue", **options)
# Draw edges
nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5)
nx.draw_networkx_edges(
G,
pos,
edgelist=[(0, 1), (1, 2), (2, 3), (3, 0)],
width=8,
alpha=0.5,
edge_color="tab:red",
)
nx.draw_networkx_edges(
G,
pos,
edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
width=8,
alpha=0.5,
edge_color="tab:blue",
)
# Add labels for nodes
labels = {0: r"$a$", 1: r"$b$", 2: r"$c$", 3: r"$d$", 4: r"$\alpha$", 5: r"$\beta$", 6: r"$\gamma$", 7: r"$\delta$"}
nx.draw_networkx_labels(G, pos, labels, font_size=22, font_color="whitesmoke")
plt.tight_layout()
plt.axis("off")
st.pyplot(plt)
elif option == "Create your own":
# Let the user input the nodes and edges of the graph
st.write("Enter the nodes and edges to create your own labeled graph.")
nodes = st.text_area("Enter node labels (comma-separated, e.g., a,b,c,d):", value="a,b,c,d")
node_labels = nodes.split(',')
edges = st.text_area("Enter edges (format: node1-node2, comma-separated, e.g., a-b,b-c):", value="a-b,b-c,c-d")
edge_list = [tuple(edge.split('-')) for edge in edges.split(',')]
# Let user choose colors for nodes and edges
node_color = st.color_picker("Pick a color for nodes:", "#FF6347")
edge_color = st.color_picker("Pick a color for edges:", "#4682B4")
# Add a button to generate the graph
if st.button("Generate Graph"):
# Generate graph based on user input
G_custom = nx.Graph()
G_custom.add_nodes_from(node_labels)
G_custom.add_edges_from(edge_list)
# Generate layout for the nodes
pos_custom = nx.spring_layout(G_custom)
# Draw the graph
nx.draw_networkx_nodes(G_custom, pos_custom, node_color=node_color, node_size=800, edgecolors="gray", alpha=0.9)
nx.draw_networkx_edges(G_custom, pos_custom, edge_color=edge_color, width=2, alpha=0.7)
# Create custom labels
custom_labels = {node: f"${node}$" for node in node_labels}
nx.draw_networkx_labels(G_custom, pos_custom, labels=custom_labels, font_size=22, font_color="whitesmoke")
plt.tight_layout()
plt.axis("off")
st.pyplot(plt)
# Display Drawing: Labels And Colors if selected
if sidebar_option == "Drawing: Labels And Colors":
display_labels_and_colors()
# Function to display Drawing: House With Colors
def display_house_with_colors():
st.title("Drawing: House With Colors")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Create the house graph and explicitly set positions
G = nx.house_graph()
pos = {0: (0, 0), 1: (1, 0), 2: (0, 1), 3: (1, 1), 4: (0.5, 2.0)}
# Plot nodes with different properties for the "wall" and "roof" nodes
nx.draw_networkx_nodes(G, pos, node_size=3000, nodelist=[0, 1, 2, 3], node_color="tab:blue")
nx.draw_networkx_nodes(G, pos, node_size=2000, nodelist=[4], node_color="tab:orange")
nx.draw_networkx_edges(G, pos, alpha=0.5, width=6)
# Customize axes
ax = plt.gca()
ax.margins(0.11)
plt.tight_layout()
plt.axis("off")
st.pyplot(plt)
elif option == "Create your own":
# Allow the user to specify node positions and colors
st.write("Specify positions for the house graph nodes.")
positions = {}
for i in range(5):
x = st.number_input(f"X-coordinate for node {i}:", min_value=-10.0, max_value=10.0, value=0.0, step=0.1)
y = st.number_input(f"Y-coordinate for node {i}:", min_value=-10.0, max_value=10.0, value=0.0, step=0.1)
positions[i] = (x, y)
# Allow the user to specify colors for wall and roof nodes
wall_color = st.color_picker("Wall color:", "#0000FF")
roof_color = st.color_picker("Roof color:", "#FFA500")
if st.button("Generate"):
# Create the house graph with the specified positions
G_custom = nx.house_graph()
# Plot nodes with user-defined properties for wall and roof nodes
nx.draw_networkx_nodes(G_custom, positions, node_size=3000, nodelist=[0, 1, 2, 3], node_color=wall_color)
nx.draw_networkx_nodes(G_custom, positions, node_size=2000, nodelist=[4], node_color=roof_color)
nx.draw_networkx_edges(G_custom, positions, alpha=0.5, width=6)
# Customize axes
ax = plt.gca()
ax.margins(0.11)
plt.tight_layout()
plt.axis("off")
st.pyplot(plt)
# Display Drawing: House With Colors if selected
if sidebar_option == "Drawing: House With Colors":
display_house_with_colors()
# Function to display Four Grids visualization for Drawing: Four Grids
def display_four_grids():
st.title("Drawing: Four Grids")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Generate a 4x4 grid graph
G = nx.grid_2d_graph(4, 4) # 4x4 grid
pos = nx.spring_layout(G, iterations=100, seed=39775)
# Create a 2x2 subplot
fig, all_axes = plt.subplots(2, 2)
ax = all_axes.flat
# Draw graphs in 4 different styles
nx.draw(G, pos, ax=ax[0], font_size=8)
nx.draw(G, pos, ax=ax[1], node_size=0, with_labels=False)
nx.draw(
G,
pos,
ax=ax[2],
node_color="tab:green",
edgecolors="tab:gray", # Node surface color
edge_color="tab:gray", # Color of graph edges
node_size=250,
with_labels=False,
width=6,
)
H = G.to_directed()
nx.draw(
H,
pos,
ax=ax[3],
node_color="tab:orange",
node_size=20,
with_labels=False,
arrowsize=10,
width=2,
)
# Set margins for the axes so that nodes aren't clipped
for a in ax:
a.margins(0.10)
fig.tight_layout()
st.pyplot(fig)
elif option == "Create your own":
# Allow the user to customize the grid dimensions
rows = st.number_input("Number of rows:", min_value=2, max_value=20, value=4)
cols = st.number_input("Number of columns:", min_value=2, max_value=20, value=4)
if st.button("Generate"):
# Generate a custom grid graph
G_custom = nx.grid_2d_graph(rows, cols) # Create the grid graph
pos = nx.spring_layout(G_custom, iterations=100, seed=39775)
# Create a 2x2 subplot
fig, all_axes = plt.subplots(2, 2)
ax = all_axes.flat
# Draw graphs in 4 different styles
nx.draw(G_custom, pos, ax=ax[0], font_size=8)
nx.draw(G_custom, pos, ax=ax[1], node_size=0, with_labels=False)
nx.draw(
G_custom,
pos,
ax=ax[2],
node_color="tab:green",
edgecolors="tab:gray", # Node surface color
edge_color="tab:gray", # Color of graph edges
node_size=250,
with_labels=False,
width=6,
)
H = G_custom.to_directed()
nx.draw(
H,
pos,
ax=ax[3],
node_color="tab:orange",
node_size=20,
with_labels=False,
arrowsize=10,
width=2,
)
# Set margins for the axes so that nodes aren't clipped
for a in ax:
a.margins(0.10)
fig.tight_layout()
st.pyplot(fig)
# Display Drawing: Four Grids if selected
if sidebar_option == "Drawing: Four Grids":
display_four_grids()
# Function to display Eigenvalue analysis for Drawing: Eigenvalues
def display_eigenvalue_analysis():
st.title("Drawing: Eigenvalues")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Generate random graph with 1000 nodes and 5000 edges
n = 1000
m = 5000
G = nx.gnm_random_graph(n, m, seed=5040) # Seed for reproducibility
# Calculate the normalized Laplacian matrix
L = nx.normalized_laplacian_matrix(G)
eigenvalues = np.linalg.eigvals(L.toarray())
# Print largest and smallest eigenvalues
st.write(f"Largest eigenvalue: {max(eigenvalues)}")
st.write(f"Smallest eigenvalue: {min(eigenvalues)}")
# Display the histogram of eigenvalues
st.write("### Eigenvalue Histogram")
plt.hist(eigenvalues, bins=100)
plt.xlim(0, 2) # Eigenvalues between 0 and 2
st.pyplot(plt)
elif option == "Create your own":
# Allow the user to customize the number of nodes and edges
n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=1000, value=100)
m_edges = st.number_input("Number of edges:", min_value=1, max_value=n_nodes*(n_nodes-1)//2, value=500)
if st.button("Generate"):
# Generate a random graph with the custom number of nodes and edges
G_custom = nx.gnm_random_graph(n_nodes, m_edges, seed=5040) # Seed for reproducibility
# Calculate the normalized Laplacian matrix
L = nx.normalized_laplacian_matrix(G_custom)
eigenvalues = np.linalg.eigvals(L.toarray())
# Print largest and smallest eigenvalues
st.write(f"Largest eigenvalue: {max(eigenvalues)}")
st.write(f"Smallest eigenvalue: {min(eigenvalues)}")
# Display the histogram of eigenvalues
st.write("### Eigenvalue Histogram")
plt.hist(eigenvalues, bins=100)
plt.xlim(0, 2) # Eigenvalues between 0 and 2
st.pyplot(plt)
# Display Drawing: Eigenvalues if selected
if sidebar_option == "Drawing: Eigenvalues":
display_eigenvalue_analysis()
# Function to display properties and graph for Basic: Properties
def display_graph_properties(G):
pathlengths = []
st.write("### Source vertex {target:length, }")
for v in G.nodes():
spl = dict(nx.single_source_shortest_path_length(G, v))
st.write(f"Vertex {v}: {spl}")
for p in spl:
pathlengths.append(spl[p])
avg_path_length = sum(pathlengths) / len(pathlengths)
st.write(f"### Average shortest path length: {avg_path_length}")
dist = {}
for p in pathlengths:
dist[p] = dist.get(p, 0) + 1
st.write("### Length #paths")
for d in sorted(dist.keys()):
st.write(f"Length {d}: {dist[d]} paths")
st.write("### Properties")
st.write(f"Radius: {nx.radius(G)}")
st.write(f"Diameter: {nx.diameter(G)}")
st.write(f"Eccentricity: {nx.eccentricity(G)}")
st.write(f"Center: {nx.center(G)}")
st.write(f"Periphery: {nx.periphery(G)}")
st.write(f"Density: {nx.density(G)}")
# Visualize the graph
st.write("### Graph Visualization")
pos = nx.spring_layout(G, seed=3068) # Seed layout for reproducibility
draw_graph(G, pos)
# Function to display graph for Basic: Read and write graphs
def display_read_write_graph(G):
st.write("### Adjacency List:")
for line in nx.generate_adjlist(G):
st.write(line)
# Write the graph's edge list to a file
st.write("### Writing Edge List to 'grid.edgelist' file:")
nx.write_edgelist(G, path="grid.edgelist", delimiter=":") # Save edge list
st.write("Edge list written to 'grid.edgelist'")
# Read the graph from the edge list
st.write("### Reading Edge List from 'grid.edgelist' file:")
H = nx.read_edgelist(path="grid.edgelist", delimiter=":")
st.write("Edge list read into graph H")
# Visualize the graph
st.write("### Graph Visualization:")
pos = nx.spring_layout(H, seed=200) # Seed for reproducibility
draw_graph(H, pos)
# Function to display Simple Graphs for Basic: Simple graph
def display_simple_graph(G, pos=None):
options = {
"font_size": 36,
"node_size": 3000,
"node_color": "white",
"edgecolors": "black",
"linewidths": 5,
"width": 5,
}
# Draw the network
nx.draw_networkx(G, pos, **options)
# Set margins for the axes so that nodes aren't clipped
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
st.pyplot(plt)
# Function to display Simple Directed Graphs for Basic: Simple graph Directed
def display_simple_directed_graph(G, pos=None):
options = {
"node_size": 500,
"node_color": "lightblue",
"arrowsize": 20,
"width": 2,
"edge_color": "gray",
}
# Draw the directed graph with the given positions and options
nx.draw_networkx(G, pos, **options)
# Set margins for the axes so that nodes aren't clipped
ax = plt.gca()
ax.margins(0.20)
plt.axis("off")
st.pyplot(plt)
# Function to display Custom Node Position Graphs for Drawing: Custom Node Position
def display_custom_node_position():
st.title("Drawing: Custom Node Position")
# Default example graph (path graph with custom node position)
G = nx.path_graph(20)
center_node = 5
edge_nodes = set(G) - {center_node}
# Ensure the nodes around the circle are evenly distributed
pos = nx.circular_layout(G.subgraph(edge_nodes))
pos[center_node] = np.array([0, 0]) # Manually specify node position
# Draw the graph
draw_graph(G, pos)
# Function to display Cluster Layout for Drawing: Cluster Layout
def display_cluster_layout():
st.title("Drawing: Cluster Layout")
G = nx.davis_southern_women_graph() # Example graph
communities = nx.community.greedy_modularity_communities(G)
# Compute positions for the node clusters as if they were themselves nodes in a supergraph using a larger scale factor
supergraph = nx.cycle_graph(len(communities))
superpos = nx.spring_layout(G, scale=50, seed=429)
# Use the "supernode" positions as the center of each node cluster
centers = list(superpos.values())
pos = {}
for center, comm in zip(centers, communities):
pos.update(nx.spring_layout(nx.subgraph(G, comm), center=center, seed=1430))
# Nodes colored by cluster
for nodes, clr in zip(communities, ("tab:blue", "tab:orange", "tab:green")):
nx.draw_networkx_nodes(G, pos=pos, nodelist=nodes, node_color=clr, node_size=100)
nx.draw_networkx_edges(G, pos=pos)
plt.tight_layout()
st.pyplot(plt)
# Function to display Degree Analysis for Drawing: Degree Analysis
def display_degree_analysis():
st.title("Drawing: Degree Analysis")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.gnp_random_graph(100, 0.02, seed=10374196)
degree_sequence = sorted((d for n, d in G.degree()), reverse=True)
dmax = max(degree_sequence)
fig = plt.figure("Degree of a random graph", figsize=(8, 8))
# Create a gridspec for adding subplots of different sizes
axgrid = fig.add_gridspec(5, 4)
ax0 = fig.add_subplot(axgrid[0:3, :])
Gcc = G.subgraph(sorted(nx.connected_components(G), key=len, reverse=True)[0])
pos = nx.spring_layout(Gcc, seed=10396953)
nx.draw_networkx_nodes(Gcc, pos, ax=ax0, node_size=20)
nx.draw_networkx_edges(Gcc, pos, ax=ax0, alpha=0.4)
ax0.set_title("Connected components of G")
ax0.set_axis_off()
ax1 = fig.add_subplot(axgrid[3:, :2])
ax1.plot(degree_sequence, "b-", marker="o")
ax1.set_title("Degree Rank Plot")
ax1.set_ylabel("Degree")
ax1.set_xlabel("Rank")
ax2 = fig.add_subplot(axgrid[3:, 2:])
ax2.bar(*np.unique(degree_sequence, return_counts=True))
ax2.set_title("Degree histogram")
ax2.set_xlabel("Degree")
ax2.set_ylabel("# of Nodes")
fig.tight_layout()
st.pyplot(fig)
elif option == "Create your own":
n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=500, value=100)
p_edge = st.slider("Edge probability:", min_value=0.0, max_value=1.0, value=0.02)
if st.button("Generate"):
if n_nodes >= 2:
G_custom = nx.gnp_random_graph(n_nodes, p_edge, seed=10374196)
degree_sequence = sorted((d for n, d in G_custom.degree()), reverse=True)
dmax = max(degree_sequence)
fig = plt.figure("Degree of a random graph", figsize=(8, 8))
# Create a gridspec for adding subplots of different sizes
axgrid = fig.add_gridspec(5, 4)
ax0 = fig.add_subplot(axgrid[0:3, :])
Gcc = G_custom.subgraph(sorted(nx.connected_components(G_custom), key=len, reverse=True)[0])
pos = nx.spring_layout(Gcc, seed=10396953)
nx.draw_networkx_nodes(Gcc, pos, ax=ax0, node_size=20)
nx.draw_networkx_edges(Gcc, pos, ax=ax0, alpha=0.4)
ax0.set_title("Connected components of G")
ax0.set_axis_off()
ax1 = fig.add_subplot(axgrid[3:, :2])
ax1.plot(degree_sequence, "b-", marker="o")
ax1.set_title("Degree Rank Plot")
ax1.set_ylabel("Degree")
ax1.set_xlabel("Rank")
ax2 = fig.add_subplot(axgrid[3:, 2:])
ax2.bar(*np.unique(degree_sequence, return_counts=True))
ax2.set_title("Degree histogram")
ax2.set_xlabel("Degree")
ax2.set_ylabel("# of Nodes")
fig.tight_layout()
st.pyplot(fig)
# Function to display Ego Graph for Drawing: Ego Graph
def display_ego_graph():
st.title("Drawing: Ego Graph")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
# Create a BA model graph - use seed for reproducibility
n = 1000
m = 2
seed = 20532
G = nx.barabasi_albert_graph(n, m, seed=seed)
# Find node with largest degree
node_and_degree = G.degree()
(largest_hub, degree) = sorted(node_and_degree, key=itemgetter(1))[-1]
# Create ego graph of main hub
hub_ego = nx.ego_graph(G, largest_hub)
# Draw graph
pos = nx.spring_layout(hub_ego, seed=seed) # Seed layout for reproducibility
nx.draw(hub_ego, pos, node_color="b", node_size=50, with_labels=False)
# Draw ego as large and red
options = {"node_size": 300, "node_color": "r"}
nx.draw_networkx_nodes(hub_ego, pos, nodelist=[largest_hub], **options)
plt.tight_layout()
st.pyplot(plt)
elif option == "Create your own":
n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=1000, value=100)
m_edges = st.number_input("Edges per node:", min_value=1, max_value=10, value=2)
if st.button("Generate"):
if n_nodes >= 2:
G_custom = nx.barabasi_albert_graph(n_nodes, m_edges, seed=20532)
# Find node with largest degree
node_and_degree = G_custom.degree()
(largest_hub, degree) = sorted(node_and_degree, key=itemgetter(1))[-1]
# Create ego graph of main hub
hub_ego = nx.ego_graph(G_custom, largest_hub)
# Draw graph
pos = nx.spring_layout(hub_ego, seed=20532) # Seed layout for reproducibility
nx.draw(hub_ego, pos, node_color="b", node_size=50, with_labels=False)
# Draw ego as large and red
options = {"node_size": 300, "node_color": "r"}
nx.draw_networkx_nodes(hub_ego, pos, nodelist=[largest_hub], **options)
plt.tight_layout()
st.pyplot(plt)
# Display Drawing: Ego Graph if selected
if sidebar_option == "Drawing: Ego Graph":
display_ego_graph()
# Display Basic: Properties if selected
elif sidebar_option == "Basic: Properties":
st.title("Basic: Properties")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.lollipop_graph(4, 6)
display_graph_properties(G)
elif option == "Create your own":
num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=50, value=5)
num_edges = st.number_input("Number of edges per group (for lollipop graph):", min_value=1, max_value=10, value=3)
if st.button("Generate"):
if num_nodes >= 2 and num_edges >= 1:
G_custom = nx.lollipop_graph(num_nodes, num_edges)
display_graph_properties(G_custom)
# Display Basic: Read and write graphs if selected
elif sidebar_option == "Basic: Read and write graphs":
st.title("Basic: Read and write graphs")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.grid_2d_graph(5, 5)
display_read_write_graph(G)
elif option == "Create your own":
rows = st.number_input("Number of rows:", min_value=2, max_value=20, value=5)
cols = st.number_input("Number of columns:", min_value=2, max_value=20, value=5)
if st.button("Generate"):
if rows >= 2 and cols >= 2:
G_custom = nx.grid_2d_graph(rows, cols)
display_read_write_graph(G_custom)
# Display Basic: Simple Graph if selected
elif sidebar_option == "Basic: Simple graph":
st.title("Basic: Simple graph")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.Graph()
G.add_edge(1, 2)
G.add_edge(1, 3)
G.add_edge(1, 5)
G.add_edge(2, 3)
G.add_edge(3, 4)
G.add_edge(4, 5)
pos = {1: (0, 0), 2: (-1, 0.3), 3: (2, 0.17), 4: (4, 0.255), 5: (5, 0.03)}
display_simple_graph(G, pos)
elif option == "Create your own":
edges = []
edge_input = st.text_area("Edges:", value="1,2\n1,3\n2,3")
if edge_input:
edge_list = edge_input.split("\n")
for edge in edge_list:
u, v = map(int, edge.split(","))
edges.append((u, v))
if st.button("Generate"):
G_custom = nx.Graph()
G_custom.add_edges_from(edges)
pos = nx.spring_layout(G_custom, seed=42)
display_simple_graph(G_custom, pos)
# Display Basic: Simple Directed Graph if selected
elif sidebar_option == "Basic: Simple graph Directed":
st.title("Basic: Simple graph Directed")
option = st.radio("Choose a graph type:", ("Default Example", "Create your own"))
if option == "Default Example":
G = nx.DiGraph([(0, 3), (1, 3), (2, 4), (3, 5), (3, 6), (4, 6), (5, 6)])
left_nodes = [0, 1, 2]
middle_nodes = [3, 4]
right_nodes = [5, 6]
pos = {n: (0, i) for i, n in enumerate(left_nodes)}
pos.update({n: (1, i + 0.5) for i, n in enumerate(middle_nodes)})
pos.update({n: (2, i + 0.5) for i, n in enumerate(right_nodes)})
display_simple_directed_graph(G, pos)
elif option == "Create your own":
edges = []
edge_input = st.text_area("Edges:", value="1,2\n1,3\n2,3")
if edge_input:
edge_list = edge_input.split("\n")
for edge in edge_list:
u, v = map(int, edge.split(","))
edges.append((u, v))
if st.button("Generate"):
G_custom = nx.DiGraph()
G_custom.add_edges_from(edges)
pos = nx.spring_layout(G_custom, seed=42)
display_simple_directed_graph(G_custom, pos)
# Display Drawing: Custom Node Position if selected
elif sidebar_option == "Drawing: Custom Node Position":
display_custom_node_position()
# Display Drawing: Cluster Layout if selected
elif sidebar_option == "Drawing: Cluster Layout":
display_cluster_layout()
# Display Drawing: Degree Analysis if selected
elif sidebar_option == "Drawing: Degree Analysis":
display_degree_analysis()