From ab2700b99fa4666e85159d58ff588a7a716ff855 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:34:31 -0400 Subject: [PATCH 01/36] Standardize --- .../algorithms/assortativity/correlation.md | 49 +++++++++------- content/algorithms/dag/index.md | 30 ++++++---- content/algorithms/euler/euler.md | 51 +++++++++------- content/algorithms/flow/dinitz_alg.md | 28 +++++---- .../facebook_notebook.md | 37 +++++++----- content/generators/geometric.md | 58 +++++++++++-------- content/generators/sudoku.md | 33 +++++++---- 7 files changed, 171 insertions(+), 115 deletions(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index c5aec79d..08eff05e 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python @@ -19,20 +19,32 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.8.5 + version: 3.9.4 --- -# Node assortativity coefficients and correlation measures +# Node Assortativity Coefficients and Correlation Measures -In this tutorial, we will go through the theory of [assortativity](https://en.wikipedia.org/wiki/Assortativity) and its measures. +In this tutorial, we will explore the theory of [assortativity](https://en.wikipedia.org/wiki/Assortativity)$^{1}$ and its measures. -Specifically, we'll focus on assortativity measures available in NetworkX at [algorithms/assortativity/correlation.py](https://github.com/networkx/networkx/blob/main/networkx/algorithms/assortativity/correlation.py): +We'll focus on assortativity measures available in NetworkX at [`algorithms/assortativity/correlation.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/assortativity/correlation.py): * Attribute assortativity * Numeric assortativity * Degree assortativity as well as mixing matrices, which are closely releated to assortativity measures. +## Import packages + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt +import pickle +import copy +import random +import warnings +%matplotlib inline +``` + ## Assortativity Assortativity in a network refers to the tendency of nodes to connect with @@ -80,7 +92,7 @@ Pearson correlation coefficient. Here the property $P(v)$ is a nominal property assigned to each node. As defined above we calculate the normalized mixing matrix $e$ and from that we -define the attribute assortativity coefficient [^1] as below. +define the attribute assortativity coefficient$^{2}$ as below. From here onwards we will use subscript notation to denote indexing, for eg. $P_i = P[i]$ and $e_{ij} = e[i][j]$ @@ -93,7 +105,7 @@ It is implemented as `attribute_assortativity_coefficient`. Here the property $P(v)$ is a numerical property assigned to each node and the definition of the normalized mixing matrix $e$, $\sigma_a$, and $\sigma_b$ are same as above. -From these we define numeric assortativity coefficient [^1] as below. +From these we define numeric assortativity coefficient $^{2}$ as below. $$ r = \frac{\sum\limits_{i,j}P_i P_j(e_{ij} -a_i b_j)}{\sigma_a\sigma_b} $$ @@ -105,7 +117,7 @@ When it comes to measuring degree assortativity for directed networks we have more options compared to assortativity w.r.t a property because we have 2 types of degrees, namely in-degree and out-degree. Based on the 2 types of degrees we can measure $2 \times 2 =4$ different types -of degree assortativity [^2]: +of degree assortativity$^{3}$: 1. r(in,in) : Measures tendency of having a directed edge (u,v) such that, in-degree(u) = in-degree(v). 2. r(in,out) : Measures tendency of having a directed edge (u,v) such that, in-degree(u) = out-degree(v). @@ -130,23 +142,14 @@ It is implemented as `degree_assortativity_coefficient` and `scipy.stats.pearsonr` to calculate the assortativity coefficient which makes it potentally faster. -## Example - -```{code-cell} ipython3 -%matplotlib inline -import networkx as nx -import matplotlib.pyplot as plt -import pickle -import copy -import random -import warnings +## Assortativity Example -warnings.filterwarnings("ignore") -``` ++++ Illustrating how value of assortativity changes ```{code-cell} ipython3 +warnings.filterwarnings("ignore") gname = "g2" # loading the graph G = nx.read_graphml(f"data/{gname}.graphml") @@ -261,6 +264,8 @@ are drawn. +++ -[^1]: M. E. J. Newman, Mixing patterns in networks +## References -[^2]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks +1. [Wikipedia, Assortativity](https://en.wikipedia.org/wiki/Assortativity) +2. M. E. J. Newman, Mixing patterns in networks +3. Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 6022e17f..e10cf20f 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python @@ -19,31 +19,35 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.2 + version: 3.9.4 --- # Directed Acyclic Graphs & Topological Sort In this tutorial, we will explore the algorithms related to a directed acyclic graph -(or a "dag" as it is sometimes called) implemented in networkx under `networkx/algorithms/dag.py`. +(or a "DAG" as it is sometimes called) implemented in networkx under [`networkx/algorithms/dag.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/dag.py). First of all, we need to understand what a directed graph is. -## Directed Graph - -### Example +## Import packages ```{code-cell} ipython3 -%matplotlib inline import networkx as nx import matplotlib.pyplot as plt +import inspect +%matplotlib inline ``` +## Directed Graphs +### Example + ```{code-cell} ipython3 +# Create graph triangle_graph = nx.from_edgelist([(1, 2), (2, 3), (3, 1)], create_using=nx.DiGraph) ``` ```{code-cell} ipython3 +# Draw graph nx.draw_planar( triangle_graph, with_labels=True, @@ -70,12 +74,14 @@ You will see this idea in action in the examples below. ### Example ```{code-cell} ipython3 +#Read in from GraphML file clothing_graph = nx.read_graphml(f"data/clothing_graph.graphml") ``` ```{code-cell} ipython3 plt.figure(figsize=(12, 12), dpi=150) +# Draw graph nx.draw_planar( clothing_graph, arrowsize=12, @@ -164,7 +170,7 @@ Then, a topological sort gives an order in which to perform the jobs. A closely related application of topological sorting algorithms was first studied in the early 1960s in the context of the -[PERT technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) +[PERT technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique)$^{1}$ for scheduling in project management. In this application, the vertices of a graph represent the milestones of a project, and the edges represent tasks that must be performed between one milestone and another. @@ -343,8 +349,6 @@ We need to check this while the `while` loop is running. Combining all of the above gives the current implementation of the `topological_generations()` function in NetworkX. ```{code-cell} ipython3 -import inspect - print(inspect.getsource(nx.topological_generations)) ``` @@ -353,3 +357,9 @@ Let's finally see what the result will be on the `clothing_graph`. ```{code-cell} ipython3 list(nx.topological_generations(clothing_graph)) ``` + +## References + +1. [Wikipedia, PERT Technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) + +2. Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index f8ff2ac9..6ea59e92 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -5,9 +5,9 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.6 + jupytext_version: 1.14.5 kernelspec: - display_name: Python 3 (ipykernel) + display_name: Python 3 language: python name: python3 --- @@ -16,9 +16,16 @@ kernelspec: +++ -In this tutorial, we will explore the Euler's algorithm and its implementation in NetworkX under `networkx/algorithms/euler.py`. +In this tutorial, we will explore the Euler's algorithm and its implementation in NetworkX under [`networkx/algorithms/euler.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/euler.py). -## Seven Bridges of Königsberg +## Import package + +```{code-cell} ipython3 +import networkx as nx +``` + +## Eulerian Paths and Circuits +### Seven Bridges of Königsberg What you are seeing below is the beautiful old town of Königsberg which is famous for its seven bridges. Each of these bridges either connect two large islands — Kneiphof and Lomse — or two mainland portions of the city. @@ -26,7 +33,7 @@ What you are seeing below is the beautiful old town of Königsberg which is famo ![image:map](images/map.png) -What gave the town its fame is a question that was asked to mathematician Leonhard Euler almost 300 years ago [^1]: +What gave the town its fame is a question that was asked to mathematician Leonhard Euler almost 300 years ago$^{2}$: > ***Can you take a walk through Königsberg visiting each mass by crossing each bridge once and only once?*** @@ -40,9 +47,8 @@ In order to have a clear look, we should first simplify the map a little. Euler observed that the choice of route inside each land mass is irrelevant. The only thing that matters is the sequence of bridges to be crossed. This observation allows us to abstract the problem even more. In the graph below, blue vertices represent the land masses and edges represent the bridges that connect them. -```{code-cell} -import networkx as nx - +```{code-cell} ipython3 +# Create graph G = nx.DiGraph() G.add_edge("A", "B", label="a") G.add_edge("B", "A", label="b") @@ -54,6 +60,7 @@ G.add_edge("C", "D", label="g") positions = {"A": (0, 0), "B": (1, -2), "C": (1, 2), "D": (2, 0)} +# Visualize graph nx.draw_networkx_nodes(G, pos=positions, node_size=500) nx.draw_networkx_edges( G, pos=positions, edgelist=[("A", "D"), ("B", "D"), ("C", "D")], arrowstyle="-" @@ -77,9 +84,10 @@ Note that every Euler Circuit is also an Euler Path. ### Euler's Method -Euler[^2] denoted land masses of the town by capital letters $A$, $B$, $C$ and $D$ and bridges by lowercase $a$, $b$, $c$, $d$, $e$, $f$ and $g$. Let's draw the graph based on this node and edge labels. +Euler$^{2}$ denoted land masses of the town by capital letters $A$, $B$, $C$ and $D$ and bridges by lowercase $a$, $b$, $c$, $d$, $e$, $f$ and $g$. Let's draw the graph based on this node and edge labels. -```{code-cell} +```{code-cell} ipython3 +# Design and draw graph edge_labels = nx.get_edge_attributes(G, "label") nx.draw_networkx_nodes(G, pos=positions, node_size=500) @@ -121,7 +129,7 @@ Euler generalized the method he applied for Königsberg problem as follows: - If there are two vertices with odd degree, then they are the starting and ending vertices. - If there are no vertices with odd degree, any vertex can be starting or ending vertex and the graph has also an Euler Circuit. -## NetworkX Implementation of Euler's Algorithm +## Implementations of Euler's Algorithm NetworkX implements several methods using the Euler's algorithm. These are: - **is_eulerian** : Whether the graph has an Eulerian circuit @@ -143,14 +151,14 @@ Implementation of the `is_eulerian` method is quite simple. In order to have an Here is an example: -```{code-cell} +```{code-cell} ipython3 T = nx.Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (2, 4)]) nx.draw( T, with_labels=True, node_size=1000, font_color="White", node_color="darkorange" ) ``` -```{code-cell} +```{code-cell} ipython3 def is_eulerian(G): if G.is_directed(): return all( @@ -160,7 +168,7 @@ def is_eulerian(G): return all(d % 2 == 0 for v, d in G.degree()) and nx.is_connected(G) ``` -```{code-cell} +```{code-cell} ipython3 is_eulerian(T) ``` @@ -249,7 +257,7 @@ For a directed graph to has an Eulerian Path, it must have Using already implemented methods, ```is_semieulerian``` simply checks if the input graph does not have an Eulerian circuit but an Eulerian path with a one line of code. -```{code-cell} +```{code-cell} ipython3 def is_semieulerian(G): return has_eulerian_path(G) and not is_eulerian(G) ``` @@ -258,24 +266,24 @@ def is_semieulerian(G): Let's call the methods above on the Seven Bridges problem. For the reasons explained above, we expect our graph to have neither an Eulerian Circuit nor an Eulerian Path. -```{code-cell} +```{code-cell} ipython3 nx.is_eulerian(G) ``` -```{code-cell} +```{code-cell} ipython3 nx.has_eulerian_path(G) ``` We can conclude this section with another example. Do you expect a wheel graph to have an Eulerian Path? -```{code-cell} +```{code-cell} ipython3 W = nx.wheel_graph(6) nx.draw(W, with_labels=True, node_size=1000, font_color="White", node_color="green") ``` The answer is No! All nodes except for the one in the center have exactly 3 edges in the wheel graph. Thus, it cannot have an Eulerian Path. -```{code-cell} +```{code-cell} ipython3 nx.has_eulerian_path(W) ``` @@ -288,5 +296,6 @@ Euler's algorithm is essential for anyone or anything that uses paths. Some exam ## References -[^1]: -[^2]: Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. +1. [Wikipedia, Seven Bridge of Konigsberg](https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg) + +2. Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index 9df18d89..6906229c 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -5,9 +5,9 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: - display_name: Python 3 (ipykernel) + display_name: Python 3 language: python name: python3 language_info: @@ -19,16 +19,17 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.10.1 + version: 3.9.4 --- -# Dinitz's algorithm and its applications -In this notebook, we will introduce the [Maximum flow problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) -and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm) [^1], which is implemented at -[algorithms/flow/dinitz_alg.py](https://github.com/networkx/networkx/blob/main/networkx/algorithms/flow/dinitz_alg.py) +# Dinitz's Algorithm and Applications +In this tutorial, we will explore the [maximum flow problem](https://en.wikipedia.org/wiki/Maximum_flow_problem)$^{1}$ +and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm)$^{2}$, which is implemented at +[`algorithms/flow/dinitz_alg.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/flow/dinitz_alg.py) in NetworkX. We will also see how it can be used to solve some interesting problems. -## Maximum flow problem + +### Import packages ```{code-cell} ipython3 import networkx as nx @@ -40,6 +41,7 @@ from copy import deepcopy from collections import deque ``` +## Maximum flow problem ### Motivation Let's say you want to send your friend some data as soon as possible, but the only way of communication/sending data between you two is through a peer-to-peer network. An @@ -50,6 +52,7 @@ you can send between a pair of nodes in this network. ```{code-cell} ipython3 # Load the example graph G = nx.read_gml("data/example_graph.gml") + # Extract info about node position from graph (for visualization) pos = {k: np.asarray(v) for k, v in G.nodes(data="pos")} label_pos = deepcopy(pos) @@ -95,8 +98,10 @@ a connection from node $u$ to node $v$ across which we can send data. There are ```{code-cell} ipython3 fig, ax = plt.subplots(figsize=(16, 8)) +# Color source and sink node node_colors = ["skyblue" if n in {"s", "t"} else "lightgray" for n in G.nodes] +# Draw graph nx.draw(G, pos, ax=ax, node_color=node_colors, with_labels=True) nx.draw_networkx_labels(G, label_pos, labels=labels, ax=ax, font_size=16) ax.set_xlim([-1.4, 1.4]); @@ -108,8 +113,10 @@ you can send from node $u$ to node $v$ is $c_{uv}$, lets call this as capacity o ```{code-cell} ipython3 fig, ax = plt.subplots(figsize=(16, 8)) +# Label capacities capacities = {(u, v): c for u, v, c in G.edges(data="capacity")} +# Draw graph nx.draw(G, pos, ax=ax, node_color=node_colors, with_labels=True) nx.draw_networkx_edge_labels(G, pos, edge_labels=capacities, ax=ax) nx.draw_networkx_labels(G, label_pos, labels=labels, ax=ax, font_size=16) @@ -540,7 +547,7 @@ fig.tight_layout() ``` Note: Iteration are stopped if the maximum flow found so far exceeds the cutoff value -## Reductions and Applications +## Applications There are many other problems which can be reduced to Maximum flow problem, for example: * [Maximum Bipartite Matching](https://en.wikipedia.org/wiki/Matching_(graph_theory)) * [Assignment Problem](https://en.wikipedia.org/wiki/Assignment_problem) @@ -635,6 +642,7 @@ Above we can see a matching of intermediate shipping points and customers which gives the maximum shipping in a day. ## References -[^1]: Dinitz' Algorithm: The Original Version and Even's Version. 2006. Yefim Dinitz. +1. [Wikipedia, Maximal Flow Problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) +2. Dinitz' Algorithm: The Original Version and Even's Version. 2006. Yefim Dinitz. In Theoretical Computer Science. Lecture Notes in Computer Science. Volume 3895. pp 218-240. diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index 4e613b33..828d6b78 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.8.3 + version: 3.9.4 --- # Facebook Network Analysis @@ -31,20 +31,22 @@ Note: Nodes $0, 107, 348, 414, 686, 698, 1684, 1912, 3437, 3980$ are the ones wh +++ -* Now, the necessary libraries are imported +### Import packages ```{code-cell} ipython3 -%matplotlib inline import pandas as pd import numpy as np import networkx as nx import matplotlib.pyplot as plt from random import randint +%matplotlib inline ``` -* The edges are loaded from the `data` folder and saved in a dataframe. Each edge is a new row and for each edge there is a `start_node` and an `end_node` column +## Analysis +The edges are loaded from the `data` folder and saved in a dataframe. Each edge is a new row and for each edge there is a `start_node` and an `end_node` column ```{code-cell} ipython3 +# Read in data facebook = pd.read_csv( "data/facebook_combined.txt.gz", compression="gzip", @@ -54,7 +56,7 @@ facebook = pd.read_csv( facebook ``` -* The graph is created from the `facebook` dataframe of the edges: +The graph is created from the `facebook` dataframe of the edges: ```{code-cell} ipython3 G = nx.from_pandas_edgelist(facebook, "start_node", "end_node") @@ -110,13 +112,13 @@ repeatable, qualitative clustering analysis. We'll revisit evaluating network clustering [later in the analysis](#clustering-effects) ## Basic topological attributes -* Total number of nodes in network: +Total number of nodes in network: ```{code-cell} ipython3 G.number_of_nodes() ``` -* Total number of edges: +Total number of edges: ```{code-cell} ipython3 G.number_of_edges() @@ -231,13 +233,13 @@ ax.set_ylabel("Frequency (%)", fontdict={"size": 22}) The majority of the shortest path lengths are from $2$ to $5$ edges long. Also, it's highly unlikely for a pair of nodes to have a shortest path of length 8 (diameter length) as the likelihood is less than $0.1$%. -* The graph's density is calculated here. Clearly, the graph is a very sparse one as: $density < 1$ +The graph's density is calculated here. Clearly, the graph is a very sparse one as: $density < 1$ ```{code-cell} ipython3 nx.density(G) ``` -* The graph's number of components are found below. As expected, the network consists of one giant compoenent: +The graph's number of components are found below. As expected, the network consists of one giant compoenent: ```{code-cell} ipython3 nx.number_connected_components(G) @@ -312,6 +314,7 @@ Looking at the results, the node $107$ has a betweenness centrality of $0.48$, m Moving on, the distribution of betweenness centralities will be plotted: ```{code-cell} ipython3 +# Set up figure plt.figure(figsize=(15, 8)) plt.hist(betweenness_centrality.values(), bins=100) plt.xticks(ticks=[0, 0.02, 0.1, 0.2, 0.3, 0.4, 0.5]) # set the x axis ticks @@ -546,7 +549,8 @@ In our case the assortativity coefficient is around $0.064$, which is almost 0. ## Network Communities A community is a group of nodes, so that nodes inside the group are connected with many more edges than between groups. Two different algorithms will be used for communities detection in this network -* Firstly, a semi-synchronous label propagation method[^1] is used to detect the communities. + +Firstly, a semi-synchronous label propagation method$^1$ is used to detect the communities. This function determines by itself the number of communities that will be detected. Now the communities will be iterated through and a colors list will be created to contain the same color for nodes that belong to the same community. Also, the number of communities is printed: @@ -573,7 +577,7 @@ nx.draw_networkx( ) ``` -* Next, the asynchronous fluid communities algorithm is used. +* Next, the asynchronous fluid communities algorithm$^{2}$ is used. With this function, we can decide the number of communities to be detected. Let's say that $8$ communities is the number we want. Again, the communities will be iterated through and a colors list will be created to contain the same color for nodes that belong to the same community. @@ -595,9 +599,10 @@ nx.draw_networkx( ) ``` -### References -[Cambridge-intelligence](https://cambridge-intelligence.com/keylines-faqs-social-network-analysis/#:~:text=Centrality%20measures%20are%20a%20vital,but%20they%20all%20work%20differently.) +## References + +1. [Semi-synchronous label propagation](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.label_propagation.label_propagation_communities.html#networkx.algorithms.community.label_propagation.label_propagation_communities) -[^1]: [Semi-synchronous label propagation](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.label_propagation.label_propagation_communities.html#networkx.algorithms.community.label_propagation.label_propagation_communities) +2. [Asynchronous fluid communities algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.asyn_fluid.asyn_fluidc.html#networkx.algorithms.community.asyn_fluid.asyn_fluidc) -[^2]: [Asynchronous fluid communities algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.asyn_fluid.asyn_fluidc.html#networkx.algorithms.community.asyn_fluid.asyn_fluidc) +[Cambridge-intelligence](https://cambridge-intelligence.com/keylines-faqs-social-network-analysis/#:~:text=Centrality%20measures%20are%20a%20vital,but%20they%20all%20work%20differently.) diff --git a/content/generators/geometric.md b/content/generators/geometric.md index 681f0b5a..6a0a6f4d 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python @@ -19,15 +19,26 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.7.4 + version: 3.9.4 --- # Geometric Generator Models In this tutorial, we'll explore the geometric network generator models -implemented in networkx under networkx/generators/geometric.py and apply them +implemented under [`networkx/generators/geometric.py`](https://github.com/networkx/networkx/blob/main/networkx/generators/geometric.py) and apply them to a real-world use case to learn how these models can be parameterized and used. +### Import packages + +```{code-cell} ipython3 +%matplotlib inline +import numpy as np +import matplotlib.pyplot as plt +import networkx as nx +``` + + + ## Geometric/Spatial Networks Many real-world complex systems have spatial components constraining the @@ -48,7 +59,7 @@ The potential application of Spatial Networks to such a wide variety of real-world systems has motivated substainial research into these networks, with many unique but closely related models being proposed with theoretical proofs for many of their network properties. -The 2010 Spatial Networks review article by Marc Barthélemy[^1] provides a +The 2010 Spatial Networks review article by Marc Barthélemy$^{1}$ provides a comprehensive overview of the field and reviews many of the most important theoretical proofs for the most common Spatial Network models. Here we explore some of the most typical Spatial Network models which have been @@ -84,10 +95,10 @@ indicates an edges exists between nodes $i$ and $j$. A d-dimensional Random Geometric Graph (RGG) is a graph where each of the $N$ nodes is assigned random coordinates in the box $[0, 1]^{d}$, and only -nodes "close" to each other are connected by an edge [^2]. +nodes "close" to each other are connected by an edge.$^{2}$ Any node within or equal to the maximum connection distance, $R$, is a connected node and the structure of the network is fully defined by $R$. -RGGs, similar to Unit Disk Graphs [^3], have been widely used to model ad-hoc +RGGs, similar to Unit Disk Graphs,$^{3}$ have been widely used to model ad-hoc wireless networks. $$ E_{ij}: d_{ij} \leq R $$ @@ -95,7 +106,7 @@ $$ E_{ij}: d_{ij} \leq R $$ #### Waxman Graphs ($\alpha$) Waxman Graphs are the spatial generalization of Erdős–Rényi random graphs, where the -probability of connection of nodes depends on a function of the distance between them[^4]. +probability of connection of nodes depends on a function of the distance between them.$^{4}$ The original edge probabiliy function proposed by Waxman is exponential in $d_{ij}$, providing two connection probability tuning parameters, $\alpha$ and $\beta$: @@ -115,7 +126,7 @@ $$ E_{ij} \propto P(d_{ij}) $$ A simple graph G is a threshold graph if we can assign weights to the vertices such that a pair of distinct vertices is adjacent exactly when the sum of their -assigned weights equals or exceeds a specified threshold, $\theta$ [^6]. +assigned weights equals or exceeds a specified threshold ($\theta$).$^{6}$ Threshold Graphs are not themselves Spatial Networks, as they do not incorporate a specific geometry or metric, but they introduce the ability to consider node weights as part of the network model which is utilized by other Spatial Network @@ -129,7 +140,7 @@ Geographical Threshold Graphs are the geographical generalization of Threshold Graphs, where a pair of vertices with weights $w_i, w_j$, and distance $d_{ij}$ are connected if and only if the product between the sum of weights $w_i$ and $w_j$ with the edge connection -function, $P(d_{ij})$, is greater than or equal to a threshold value, $\theta$. [^8] +function, $P(d_{ij})$, is greater than or equal to a threshold value, $\theta$.$^{8}$ $$ E_{ij}: (w_i + w_j) P(d_{ij}) \geq \theta $$ @@ -140,8 +151,8 @@ between nodes that are within the maximum connection distance, $R$, to better mo real-world systems where node proximity does not necessarily guarantee a connection between "close" nodes. In Soft Random Geometric Graphs, the probability of connection between nodes $i$ -and $j$ is a function of their distance, $d_{ij}, if $d_{ij} \leq R$. -Otherwise, they are disconnected [^7]. +and $j$ is a function of their distance, $d_{ij}$, if $d_{ij} \leq R$. +Otherwise, they are disconnected.$^{7}$ $$ E_{ij} \propto P(d_{ij}) \textrm{ if } d_{ij} \leq R $$ @@ -149,7 +160,7 @@ $$ E_{ij} \propto P(d_{ij}) \textrm{ if } d_{ij} \leq R $$ Thresholded Random Geometric Graphs extend RGGs to incorporate node weights into the model, where connections are only made between nodes with sufficiently -powerful weights, up to a maximum connection distance between nodes [^9]. +powerful weights, up to a maximum connection distance between nodes.$^{9}$ $$ (w_i + w_j) \geq \theta \textrm{ if } d_{ij} \leq R $$ @@ -173,11 +184,6 @@ With this dataset, we can model the supercharger network with the various spatia networks implemented in networkx. ```{code-cell} ipython3 -%matplotlib inline -import numpy as np -import matplotlib.pyplot as plt -import networkx as nx - # Some matplotlib settings mpl_params = { "axes.titlesize": 20, @@ -344,18 +350,20 @@ for thresh, ax in zip(thresholds, axes): fig.tight_layout() ``` -[^1]: Spatial Networks +## References + +1. Spatial Networks -[^2]: Random Geometric Graphs +2. Random Geometric Graphs -[^3]: Unit Disk Graphs +3. Unit Disk Graphs -[^4]: Waxman Graphs +4. Waxman Graphs -[^6]: Threshold Graphs - +6. Threshold Graphs - -[^7]: Soft Geometric Random Graphs - +7. Soft Geometric Random Graphs - -[^8]: Geometric Threshold Graphs - +8. Geometric Threshold Graphs - -[^9]: Thresholded Random Geometric Graphs - +9. Thresholded Random Geometric Graphs - diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index a4eef879..c2645a2a 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -5,9 +5,9 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: - display_name: Python 3 (ipykernel) + display_name: Python 3 language: python name: python3 language_info: @@ -19,14 +19,24 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.7 + version: 3.9.4 --- -+++ {"tags": []} - # Sudoku and Graph coloring -## Introduction and intuition building +In this tutorial, we will apply graph theory to the problem of solving a Sudoku with NetworkX. + +### Import packages + +```{code-cell} ipython3 +import numpy as np +import matplotlib as mpl +import matplotlib.pyplot as plt +import networkx as nx +``` + + +## Introduction and Intuition Sudoku is a popular number-placement puzzle based on logic and combinatorics. The objective is to fill a 9 × 9 grid with digits such that each column, each row, and each of the nine 3 × 3 subgrids that compose the grid contain all of the digits from 1 to 9 (once and only once). Usually the puzzle is partially filled in a way that guarantees a unique solution, as of now from what we know at least 17 cues are needed to create a puzzle with a unique solution. @@ -68,8 +78,8 @@ Now, from (1) we can get that the graph of a Sudoku grid of rank 3 is a $(V=81, Let's take an example Sudoku Puzzle that we will solve with graph theory (NetworkX and some cool figures as well!) ```{code-cell} ipython3 -import numpy as np +# Create Sudoku puzzle puzzle = np.asarray( [ [0, 4, 3, 0, 8, 0, 2, 5, 0], @@ -86,10 +96,6 @@ puzzle = np.asarray( ``` ```{code-cell} ipython3 -import matplotlib as mpl -import matplotlib.pyplot as plt -import networkx as nx - n = 3 G = nx.sudoku_graph(n) mapping = dict(zip(G.nodes(), puzzle.flatten())) @@ -100,6 +106,7 @@ low, *_, high = sorted(mapping.values()) norm = mpl.colors.Normalize(vmin=low, vmax=high, clip=True) mapper = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.Pastel1) +# draw the graph plt.figure(figsize=(12, 12)) nx.draw( G, @@ -256,3 +263,7 @@ pretty! Now, let's check how do sudoku graphs look if sudokus were 16 x 16 grids ```{code-cell} ipython3 plot_edge_colored_sudoku(n=4) ``` + +## References + +[Wikipedia - Sudoku Graph](https://en.wikipedia.org/wiki/Sudoku_graph) From 30b54ee29a0ff0b18ce75727c75c85b91726dd6d Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:36:36 -0400 Subject: [PATCH 02/36] Update sudoku.md --- content/generators/sudoku.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index c2645a2a..d9ae4c47 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -22,7 +22,7 @@ language_info: version: 3.9.4 --- -# Sudoku and Graph coloring +# Sudoku and Graph Coloring In this tutorial, we will apply graph theory to the problem of solving a Sudoku with NetworkX. From 2b9b75d05fc55397340ab8af4dcc65c0afdd673d Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Fri, 24 Mar 2023 01:19:35 -0400 Subject: [PATCH 03/36] Changed links back to old form --- .../algorithms/assortativity/correlation.md | 16 +++++---- content/algorithms/dag/index.md | 8 ++--- content/algorithms/euler/euler.md | 4 +-- content/algorithms/flow/dinitz_alg.md | 9 ++--- .../facebook_notebook.md | 10 +++--- content/generators/geometric.md | 36 +++++++++---------- 6 files changed, 41 insertions(+), 42 deletions(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 08eff05e..44b14ad7 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -24,7 +24,7 @@ language_info: # Node Assortativity Coefficients and Correlation Measures -In this tutorial, we will explore the theory of [assortativity](https://en.wikipedia.org/wiki/Assortativity)$^{1}$ and its measures. +In this tutorial, we will explore the theory of [assortativity](https://en.wikipedia.org/wiki/Assortativity) [^1] and its measures. We'll focus on assortativity measures available in NetworkX at [`algorithms/assortativity/correlation.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/assortativity/correlation.py): * Attribute assortativity @@ -92,7 +92,7 @@ Pearson correlation coefficient. Here the property $P(v)$ is a nominal property assigned to each node. As defined above we calculate the normalized mixing matrix $e$ and from that we -define the attribute assortativity coefficient$^{2}$ as below. +define the attribute assortativity coefficient [^2] as below. From here onwards we will use subscript notation to denote indexing, for eg. $P_i = P[i]$ and $e_{ij} = e[i][j]$ @@ -105,7 +105,7 @@ It is implemented as `attribute_assortativity_coefficient`. Here the property $P(v)$ is a numerical property assigned to each node and the definition of the normalized mixing matrix $e$, $\sigma_a$, and $\sigma_b$ are same as above. -From these we define numeric assortativity coefficient $^{2}$ as below. +From these we define numeric assortativity coefficient [^2] as below. $$ r = \frac{\sum\limits_{i,j}P_i P_j(e_{ij} -a_i b_j)}{\sigma_a\sigma_b} $$ @@ -117,7 +117,7 @@ When it comes to measuring degree assortativity for directed networks we have more options compared to assortativity w.r.t a property because we have 2 types of degrees, namely in-degree and out-degree. Based on the 2 types of degrees we can measure $2 \times 2 =4$ different types -of degree assortativity$^{3}$: +of degree assortativity [^3]: 1. r(in,in) : Measures tendency of having a directed edge (u,v) such that, in-degree(u) = in-degree(v). 2. r(in,out) : Measures tendency of having a directed edge (u,v) such that, in-degree(u) = out-degree(v). @@ -266,6 +266,8 @@ are drawn. ## References -1. [Wikipedia, Assortativity](https://en.wikipedia.org/wiki/Assortativity) -2. M. E. J. Newman, Mixing patterns in networks -3. Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks +[^1]: [Wikipedia, Assortativity](https://en.wikipedia.org/wiki/Assortativity) + +[^2]: M. E. J. Newman, Mixing patterns in networks + +[3^]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index e10cf20f..bd67a28b 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -25,7 +25,7 @@ language_info: # Directed Acyclic Graphs & Topological Sort In this tutorial, we will explore the algorithms related to a directed acyclic graph -(or a "DAG" as it is sometimes called) implemented in networkx under [`networkx/algorithms/dag.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/dag.py). +(or a "DAG" as it is sometimes called) implemented in NetworkX under [`networkx/algorithms/dag.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/dag.py). First of all, we need to understand what a directed graph is. @@ -170,7 +170,7 @@ Then, a topological sort gives an order in which to perform the jobs. A closely related application of topological sorting algorithms was first studied in the early 1960s in the context of the -[PERT technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique)$^{1}$ +[PERT technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) [^1] for scheduling in project management. In this application, the vertices of a graph represent the milestones of a project, and the edges represent tasks that must be performed between one milestone and another. @@ -360,6 +360,4 @@ list(nx.topological_generations(clothing_graph)) ## References -1. [Wikipedia, PERT Technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) - -2. Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. +[^1]: [Wikipedia, PERT Technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index 6ea59e92..3abcbaf6 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -33,7 +33,7 @@ What you are seeing below is the beautiful old town of Königsberg which is famo ![image:map](images/map.png) -What gave the town its fame is a question that was asked to mathematician Leonhard Euler almost 300 years ago$^{2}$: +What gave the town its fame is a question that was asked to mathematician Leonhard Euler almost 300 years ago [^1]: > ***Can you take a walk through Königsberg visiting each mass by crossing each bridge once and only once?*** @@ -84,7 +84,7 @@ Note that every Euler Circuit is also an Euler Path. ### Euler's Method -Euler$^{2}$ denoted land masses of the town by capital letters $A$, $B$, $C$ and $D$ and bridges by lowercase $a$, $b$, $c$, $d$, $e$, $f$ and $g$. Let's draw the graph based on this node and edge labels. +Euler [^2] denoted land masses of the town by capital letters $A$, $B$, $C$ and $D$ and bridges by lowercase $a$, $b$, $c$, $d$, $e$, $f$ and $g$. Let's draw the graph based on this node and edge labels. ```{code-cell} ipython3 # Design and draw graph diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index 6906229c..76f781ce 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -23,8 +23,8 @@ language_info: --- # Dinitz's Algorithm and Applications -In this tutorial, we will explore the [maximum flow problem](https://en.wikipedia.org/wiki/Maximum_flow_problem)$^{1}$ -and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm)$^{2}$, which is implemented at +In this tutorial, we will explore the [maximum flow problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) [^1] +and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm) [^2], which is implemented at [`algorithms/flow/dinitz_alg.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/flow/dinitz_alg.py) in NetworkX. We will also see how it can be used to solve some interesting problems. @@ -642,7 +642,8 @@ Above we can see a matching of intermediate shipping points and customers which gives the maximum shipping in a day. ## References -1. [Wikipedia, Maximal Flow Problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) -2. Dinitz' Algorithm: The Original Version and Even's Version. 2006. Yefim Dinitz. +[^1]: [Wikipedia, Maximal Flow Problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) + +[^2]: Dinitz' Algorithm: The Original Version and Even's Version. 2006. Yefim Dinitz. In Theoretical Computer Science. Lecture Notes in Computer Science. Volume 3895. pp 218-240. diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index 828d6b78..f0120edd 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -23,7 +23,7 @@ language_info: --- # Facebook Network Analysis -This notebook contains a social network analysis mainly executed with the library of NetworkX. In detail, the facebook circles (friends lists) of ten people will be examined and scrutinized in order to extract all kinds of valuable information. The dataset can be found in the [stanford website](http://snap.stanford.edu/data/ego-Facebook.html). Moreover, as known, a facebook network is undirected and has no weights because one user can become friends with another user just once. Looking at the dataset from a graph analysis perspective: +This notebook contains a social network analysis mainly executed with the library of NetworkX. In detail, the facebook circles (friends lists) of ten people will be examined and scrutinized in order to extract all kinds of valuable information. The dataset can be found at this link: [Stanford Facebook Dataset](http://snap.stanford.edu/data/ego-Facebook.html). Moreover, as known, a facebook network is undirected and has no weights because one user can become friends with another user just once. Looking at the dataset from a graph analysis perspective: * Each node represents an anonymized facebook user that belongs to one of those ten friends lists. * Each edge corresponds to the friendship of two facebook users that belong to this network. In other words, two users must become friends on facebook in order for them to be connected in the particular network. @@ -550,7 +550,7 @@ In our case the assortativity coefficient is around $0.064$, which is almost 0. ## Network Communities A community is a group of nodes, so that nodes inside the group are connected with many more edges than between groups. Two different algorithms will be used for communities detection in this network -Firstly, a semi-synchronous label propagation method$^1$ is used to detect the communities. +Firstly, a semi-synchronous label propagation method [^1] is used to detect the communities. This function determines by itself the number of communities that will be detected. Now the communities will be iterated through and a colors list will be created to contain the same color for nodes that belong to the same community. Also, the number of communities is printed: @@ -577,7 +577,7 @@ nx.draw_networkx( ) ``` -* Next, the asynchronous fluid communities algorithm$^{2}$ is used. +* Next, the asynchronous fluid communities algorithm [^2] is used. With this function, we can decide the number of communities to be detected. Let's say that $8$ communities is the number we want. Again, the communities will be iterated through and a colors list will be created to contain the same color for nodes that belong to the same community. @@ -601,8 +601,8 @@ nx.draw_networkx( ## References -1. [Semi-synchronous label propagation](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.label_propagation.label_propagation_communities.html#networkx.algorithms.community.label_propagation.label_propagation_communities) +[^1]: [Semi-synchronous label propagation](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.label_propagation.label_propagation_communities.html#networkx.algorithms.community.label_propagation.label_propagation_communities) -2. [Asynchronous fluid communities algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.asyn_fluid.asyn_fluidc.html#networkx.algorithms.community.asyn_fluid.asyn_fluidc) +[^2]: [Asynchronous fluid communities algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.community.asyn_fluid.asyn_fluidc.html#networkx.algorithms.community.asyn_fluid.asyn_fluidc) [Cambridge-intelligence](https://cambridge-intelligence.com/keylines-faqs-social-network-analysis/#:~:text=Centrality%20measures%20are%20a%20vital,but%20they%20all%20work%20differently.) diff --git a/content/generators/geometric.md b/content/generators/geometric.md index 6a0a6f4d..a777b0e4 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -37,8 +37,6 @@ import matplotlib.pyplot as plt import networkx as nx ``` - - ## Geometric/Spatial Networks Many real-world complex systems have spatial components constraining the @@ -59,7 +57,7 @@ The potential application of Spatial Networks to such a wide variety of real-world systems has motivated substainial research into these networks, with many unique but closely related models being proposed with theoretical proofs for many of their network properties. -The 2010 Spatial Networks review article by Marc Barthélemy$^{1}$ provides a +The 2010 Spatial Networks review article by Marc Barthélemy [^1] provides a comprehensive overview of the field and reviews many of the most important theoretical proofs for the most common Spatial Network models. Here we explore some of the most typical Spatial Network models which have been @@ -95,10 +93,10 @@ indicates an edges exists between nodes $i$ and $j$. A d-dimensional Random Geometric Graph (RGG) is a graph where each of the $N$ nodes is assigned random coordinates in the box $[0, 1]^{d}$, and only -nodes "close" to each other are connected by an edge.$^{2}$ +nodes "close" to each other are connected by an edge [^2]. Any node within or equal to the maximum connection distance, $R$, is a connected node and the structure of the network is fully defined by $R$. -RGGs, similar to Unit Disk Graphs,$^{3}$ have been widely used to model ad-hoc +RGGs, similar to Unit Disk Graphs [^3], have been widely used to model ad-hoc wireless networks. $$ E_{ij}: d_{ij} \leq R $$ @@ -106,7 +104,7 @@ $$ E_{ij}: d_{ij} \leq R $$ #### Waxman Graphs ($\alpha$) Waxman Graphs are the spatial generalization of Erdős–Rényi random graphs, where the -probability of connection of nodes depends on a function of the distance between them.$^{4}$ +probability of connection of nodes depends on a function of the distance between them[^4]. The original edge probabiliy function proposed by Waxman is exponential in $d_{ij}$, providing two connection probability tuning parameters, $\alpha$ and $\beta$: @@ -126,7 +124,7 @@ $$ E_{ij} \propto P(d_{ij}) $$ A simple graph G is a threshold graph if we can assign weights to the vertices such that a pair of distinct vertices is adjacent exactly when the sum of their -assigned weights equals or exceeds a specified threshold ($\theta$).$^{6}$ +assigned weights equals or exceeds a specified threshold, $\theta$ [^6]. Threshold Graphs are not themselves Spatial Networks, as they do not incorporate a specific geometry or metric, but they introduce the ability to consider node weights as part of the network model which is utilized by other Spatial Network @@ -140,7 +138,7 @@ Geographical Threshold Graphs are the geographical generalization of Threshold Graphs, where a pair of vertices with weights $w_i, w_j$, and distance $d_{ij}$ are connected if and only if the product between the sum of weights $w_i$ and $w_j$ with the edge connection -function, $P(d_{ij})$, is greater than or equal to a threshold value, $\theta$.$^{8}$ +function, $P(d_{ij})$, is greater than or equal to a threshold value, $\theta$. [^8] $$ E_{ij}: (w_i + w_j) P(d_{ij}) \geq \theta $$ @@ -151,8 +149,8 @@ between nodes that are within the maximum connection distance, $R$, to better mo real-world systems where node proximity does not necessarily guarantee a connection between "close" nodes. In Soft Random Geometric Graphs, the probability of connection between nodes $i$ -and $j$ is a function of their distance, $d_{ij}$, if $d_{ij} \leq R$. -Otherwise, they are disconnected.$^{7}$ +and $j$ is a function of their distance, $d_{ij}, if $d_{ij} \leq R$. +Otherwise, they are disconnected [^7]. $$ E_{ij} \propto P(d_{ij}) \textrm{ if } d_{ij} \leq R $$ @@ -160,7 +158,7 @@ $$ E_{ij} \propto P(d_{ij}) \textrm{ if } d_{ij} \leq R $$ Thresholded Random Geometric Graphs extend RGGs to incorporate node weights into the model, where connections are only made between nodes with sufficiently -powerful weights, up to a maximum connection distance between nodes.$^{9}$ +powerful weights, up to a maximum connection distance between nodes [^9]. $$ (w_i + w_j) \geq \theta \textrm{ if } d_{ij} \leq R $$ @@ -352,18 +350,18 @@ fig.tight_layout() ## References -1. Spatial Networks +[^1]: Spatial Networks -2. Random Geometric Graphs +[^2]: Random Geometric Graphs -3. Unit Disk Graphs +[^3]: Unit Disk Graphs -4. Waxman Graphs +[^4]: Waxman Graphs -6. Threshold Graphs - +[^6]: Threshold Graphs - -7. Soft Geometric Random Graphs - +[^7]: Soft Geometric Random Graphs - -8. Geometric Threshold Graphs - +[^8]: Geometric Threshold Graphs - -9. Thresholded Random Geometric Graphs - +[^9]: Thresholded Random Geometric Graphs - From be1bc45bf0d7998ff59b0eb95c4eb7d48190fbea Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Fri, 24 Mar 2023 02:01:27 -0400 Subject: [PATCH 04/36] change versions back to original --- content/algorithms/assortativity/correlation.md | 4 ++-- content/algorithms/dag/index.md | 4 ++-- content/algorithms/euler/euler.md | 2 +- content/algorithms/flow/dinitz_alg.md | 4 ++-- content/exploratory_notebooks/facebook_notebook.md | 4 ++-- content/generators/geometric.md | 4 ++-- content/generators/sudoku.md | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 44b14ad7..376a4fb5 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.8.5 --- # Node Assortativity Coefficients and Correlation Measures diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index bd67a28b..dd9847a5 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.9.2 --- # Directed Acyclic Graphs & Topological Sort diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index 3abcbaf6..c84184e5 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.6 kernelspec: display_name: Python 3 language: python diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index 76f781ce..a82c1010 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.10.1 --- # Dinitz's Algorithm and Applications diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index f0120edd..99ebab37 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.8.3 --- # Facebook Network Analysis diff --git a/content/generators/geometric.md b/content/generators/geometric.md index a777b0e4..0caee576 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: display_name: Python 3 language: python @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.7.4 --- # Geometric Generator Models diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index d9ae4c47..a61e6594 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -5,9 +5,9 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.13.8 kernelspec: - display_name: Python 3 + display_name: Python 3 (ipykernel) language: python name: python3 language_info: @@ -19,7 +19,7 @@ language_info: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.9.4 + version: 3.9.7 --- # Sudoku and Graph Coloring From fc017a2abb93258419646e79c4f42ed9241ae348 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:43:40 -0400 Subject: [PATCH 05/36] add back ipykernel --- content/algorithms/euler/euler.md | 2 +- content/algorithms/flow/dinitz_alg.md | 2 +- content/generators/sudoku.md | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index c84184e5..686dd714 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -7,7 +7,7 @@ jupytext: format_version: 0.13 jupytext_version: 1.13.6 kernelspec: - display_name: Python 3 + display_name: Python 3 (ipykernel) language: python name: python3 --- diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index a82c1010..9b5c57a2 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -7,7 +7,7 @@ jupytext: format_version: 0.13 jupytext_version: 1.13.8 kernelspec: - display_name: Python 3 + display_name: Python 3 (ipykernel) language: python name: python3 language_info: diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index a61e6594..3a8a139d 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -22,6 +22,8 @@ language_info: version: 3.9.7 --- ++++ {"tags": []} + # Sudoku and Graph Coloring In this tutorial, we will apply graph theory to the problem of solving a Sudoku with NetworkX. From 5ed2872dc973573206eed0220ca730d4c217a196 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Fri, 31 Mar 2023 15:30:34 +0530 Subject: [PATCH 06/36] clean up, make pre-commit happy --- .../algorithms/assortativity/correlation.md | 2 +- content/algorithms/dag/index.md | 2 +- content/algorithms/euler/euler.md | 24 +++++++++---------- content/algorithms/flow/dinitz_alg.md | 2 +- content/algorithms/index.md | 3 +++ .../facebook_notebook.md | 2 +- content/generators/geometric.md | 2 +- content/generators/index.md | 3 +++ content/generators/sudoku.md | 6 +---- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 376a4fb5..5208da66 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index dd9847a5..1f297172 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index 686dd714..1079badf 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.6 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -20,7 +20,7 @@ In this tutorial, we will explore the Euler's algorithm and its implementation i ## Import package -```{code-cell} ipython3 +```{code-cell} import networkx as nx ``` @@ -47,7 +47,7 @@ In order to have a clear look, we should first simplify the map a little. Euler observed that the choice of route inside each land mass is irrelevant. The only thing that matters is the sequence of bridges to be crossed. This observation allows us to abstract the problem even more. In the graph below, blue vertices represent the land masses and edges represent the bridges that connect them. -```{code-cell} ipython3 +```{code-cell} # Create graph G = nx.DiGraph() G.add_edge("A", "B", label="a") @@ -86,7 +86,7 @@ Note that every Euler Circuit is also an Euler Path. Euler [^2] denoted land masses of the town by capital letters $A$, $B$, $C$ and $D$ and bridges by lowercase $a$, $b$, $c$, $d$, $e$, $f$ and $g$. Let's draw the graph based on this node and edge labels. -```{code-cell} ipython3 +```{code-cell} # Design and draw graph edge_labels = nx.get_edge_attributes(G, "label") @@ -151,14 +151,14 @@ Implementation of the `is_eulerian` method is quite simple. In order to have an Here is an example: -```{code-cell} ipython3 +```{code-cell} T = nx.Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (2, 3), (2, 4)]) nx.draw( T, with_labels=True, node_size=1000, font_color="White", node_color="darkorange" ) ``` -```{code-cell} ipython3 +```{code-cell} def is_eulerian(G): if G.is_directed(): return all( @@ -168,7 +168,7 @@ def is_eulerian(G): return all(d % 2 == 0 for v, d in G.degree()) and nx.is_connected(G) ``` -```{code-cell} ipython3 +```{code-cell} is_eulerian(T) ``` @@ -257,7 +257,7 @@ For a directed graph to has an Eulerian Path, it must have Using already implemented methods, ```is_semieulerian``` simply checks if the input graph does not have an Eulerian circuit but an Eulerian path with a one line of code. -```{code-cell} ipython3 +```{code-cell} def is_semieulerian(G): return has_eulerian_path(G) and not is_eulerian(G) ``` @@ -266,24 +266,24 @@ def is_semieulerian(G): Let's call the methods above on the Seven Bridges problem. For the reasons explained above, we expect our graph to have neither an Eulerian Circuit nor an Eulerian Path. -```{code-cell} ipython3 +```{code-cell} nx.is_eulerian(G) ``` -```{code-cell} ipython3 +```{code-cell} nx.has_eulerian_path(G) ``` We can conclude this section with another example. Do you expect a wheel graph to have an Eulerian Path? -```{code-cell} ipython3 +```{code-cell} W = nx.wheel_graph(6) nx.draw(W, with_labels=True, node_size=1000, font_color="White", node_color="green") ``` The answer is No! All nodes except for the one in the center have exactly 3 edges in the wheel graph. Thus, it cannot have an Eulerian Path. -```{code-cell} ipython3 +```{code-cell} nx.has_eulerian_path(W) ``` diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index 9b5c57a2..de0dccf3 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 (ipykernel) language: python diff --git a/content/algorithms/index.md b/content/algorithms/index.md index cd233db9..1d31c70e 100644 --- a/content/algorithms/index.md +++ b/content/algorithms/index.md @@ -1,5 +1,8 @@ + ++++ + # Algorithms A closer look at some of the algorithms and network analysis techniques diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index 99ebab37..d304c191 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python diff --git a/content/generators/geometric.md b/content/generators/geometric.md index 0caee576..263ad8d9 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 language: python diff --git a/content/generators/index.md b/content/generators/index.md index ad4d3028..e6619aaf 100644 --- a/content/generators/index.md +++ b/content/generators/index.md @@ -1,5 +1,8 @@ + ++++ + # Graph Generators A closer look at the functions provided by NetworkX to create interesting diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index 3a8a139d..6c785a32 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.13.8 + jupytext_version: 1.14.5 kernelspec: display_name: Python 3 (ipykernel) language: python @@ -22,8 +22,6 @@ language_info: version: 3.9.7 --- -+++ {"tags": []} - # Sudoku and Graph Coloring In this tutorial, we will apply graph theory to the problem of solving a Sudoku with NetworkX. @@ -37,7 +35,6 @@ import matplotlib.pyplot as plt import networkx as nx ``` - ## Introduction and Intuition Sudoku is a popular number-placement puzzle based on logic and combinatorics. The objective is to fill a 9 × 9 grid with digits such that each column, each row, and each of the nine 3 × 3 subgrids that compose the grid contain all of the digits from 1 to 9 (once and only once). Usually the puzzle is partially filled in a way that guarantees a unique solution, as of now from what we know at least 17 cues are needed to create a puzzle with a unique solution. @@ -80,7 +77,6 @@ Now, from (1) we can get that the graph of a Sudoku grid of rank 3 is a $(V=81, Let's take an example Sudoku Puzzle that we will solve with graph theory (NetworkX and some cool figures as well!) ```{code-cell} ipython3 - # Create Sudoku puzzle puzzle = np.asarray( [ From dff5fab04172a7943c1e288dea8424cfa8ac6cc0 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Fri, 31 Mar 2023 15:40:28 +0530 Subject: [PATCH 07/36] make pre-commit happy --- content/algorithms/assortativity/correlation.md | 1 + content/algorithms/dag/index.md | 3 ++- content/algorithms/index.md | 2 ++ content/exploratory_notebooks/facebook_notebook.md | 1 + content/generators/index.md | 2 ++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 5208da66..0fbc08fd 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -42,6 +42,7 @@ import pickle import copy import random import warnings + %matplotlib inline ``` diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 1f297172..5f3a3c7d 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -35,6 +35,7 @@ First of all, we need to understand what a directed graph is. import networkx as nx import matplotlib.pyplot as plt import inspect + %matplotlib inline ``` @@ -74,7 +75,7 @@ You will see this idea in action in the examples below. ### Example ```{code-cell} ipython3 -#Read in from GraphML file +# Read in from GraphML file clothing_graph = nx.read_graphml(f"data/clothing_graph.graphml") ``` diff --git a/content/algorithms/index.md b/content/algorithms/index.md index 49b78711..f23367f6 100644 --- a/content/algorithms/index.md +++ b/content/algorithms/index.md @@ -1,6 +1,8 @@ ++++ + +++ # Algorithms diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index d304c191..b9159c9c 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -39,6 +39,7 @@ import numpy as np import networkx as nx import matplotlib.pyplot as plt from random import randint + %matplotlib inline ``` diff --git a/content/generators/index.md b/content/generators/index.md index e6619aaf..94d9991d 100644 --- a/content/generators/index.md +++ b/content/generators/index.md @@ -1,6 +1,8 @@ ++++ + +++ # Graph Generators From de75f820b4eacf97f20c4367da2c33235facfd41 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:38:12 -0700 Subject: [PATCH 08/36] Fix typo. --- content/algorithms/assortativity/correlation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 0fbc08fd..88640e37 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -31,7 +31,7 @@ We'll focus on assortativity measures available in NetworkX at [`algorithms/asso * Numeric assortativity * Degree assortativity -as well as mixing matrices, which are closely releated to assortativity measures. +as well as mixing matrices, which are closely related to assortativity measures. ## Import packages From 28e89c4dc301c4c18d9ccdad7122f5eb02f0e164 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:44:07 -0700 Subject: [PATCH 09/36] Rm unnecessary warnings filter. --- content/algorithms/assortativity/correlation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 88640e37..9afdebc1 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -150,7 +150,6 @@ it potentally faster. Illustrating how value of assortativity changes ```{code-cell} ipython3 -warnings.filterwarnings("ignore") gname = "g2" # loading the graph G = nx.read_graphml(f"data/{gname}.graphml") From 8ba46f39a52783911b0dde88b94e1b926e10b52e Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:44:35 -0700 Subject: [PATCH 10/36] Fix footnote format. --- content/algorithms/assortativity/correlation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 9afdebc1..c1fed531 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -270,4 +270,4 @@ are drawn. [^2]: M. E. J. Newman, Mixing patterns in networks -[3^]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks +[^3]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks From 4ecf27ebb558ab821ca56740089726990e7e6dd4 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:46:06 -0700 Subject: [PATCH 11/36] Condense heading. --- content/algorithms/dag/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 5f3a3c7d..36431579 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -39,8 +39,7 @@ import inspect %matplotlib inline ``` -## Directed Graphs -### Example +## Example: Directed Graphs ```{code-cell} ipython3 # Create graph From 6cc9a46195940e94e9f8d7ccabd78e3ff266efe1 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:48:25 -0700 Subject: [PATCH 12/36] More canonical way to create DiGraph. --- content/algorithms/dag/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 36431579..9473dae9 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -43,7 +43,7 @@ import inspect ```{code-cell} ipython3 # Create graph -triangle_graph = nx.from_edgelist([(1, 2), (2, 3), (3, 1)], create_using=nx.DiGraph) +triangle_graph = nx.DiGraph([(1, 2), (2, 3), (3, 1)]) ``` ```{code-cell} ipython3 From 41de46eed33dbd56a378f28c5f634c37158aeb0e Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:51:51 -0700 Subject: [PATCH 13/36] Condense headings. --- content/algorithms/euler/euler.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index 1079badf..d67de25e 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -24,8 +24,7 @@ In this tutorial, we will explore the Euler's algorithm and its implementation i import networkx as nx ``` -## Eulerian Paths and Circuits -### Seven Bridges of Königsberg +## Seven Bridges of Königsberg What you are seeing below is the beautiful old town of Königsberg which is famous for its seven bridges. Each of these bridges either connect two large islands — Kneiphof and Lomse — or two mainland portions of the city. From e0169a853f252a0a73f33744601799b88524fab3 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:56:11 -0700 Subject: [PATCH 14/36] Fix header level in flow. --- content/algorithms/flow/dinitz_alg.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index 666c3a26..de3a5d41 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -29,7 +29,7 @@ and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm) [^2] in NetworkX. We will also see how it can be used to solve some interesting problems. -### Import packages +## Import packages ```{code-cell} ipython3 import networkx as nx @@ -42,7 +42,9 @@ from collections import deque ``` ## Maximum flow problem + ### Motivation + Let's say you want to send your friend some data as soon as possible, but the only way of communication/sending data between you two is through a peer-to-peer network. An interesting thing about this peer-to-peer network is that it allows you to send data From 0fb7a96301d09559e1b346a5604a26502935a407 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:57:39 -0700 Subject: [PATCH 15/36] Typo. --- content/exploratory_notebooks/facebook_notebook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index b9159c9c..24f0d4a8 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -240,7 +240,7 @@ The graph's density is calculated here. Clearly, the graph is a very sparse one nx.density(G) ``` -The graph's number of components are found below. As expected, the network consists of one giant compoenent: +The graph's number of components are found below. As expected, the network consists of one giant component: ```{code-cell} ipython3 nx.number_connected_components(G) From e12e5280c1100dd63102e0bba26a978658421a7a Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 20:59:12 -0700 Subject: [PATCH 16/36] Fix heading level. --- content/generators/sudoku.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index 6c785a32..4acc6630 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -26,7 +26,7 @@ language_info: In this tutorial, we will apply graph theory to the problem of solving a Sudoku with NetworkX. -### Import packages +## Import packages ```{code-cell} ipython3 import numpy as np From 66ec90ce9eecb73c2710e292b27e75ca36a1e23c Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Sat, 8 Apr 2023 21:09:45 -0700 Subject: [PATCH 17/36] More heading fixes. --- content/exploratory_notebooks/facebook_notebook.md | 2 +- content/generators/geometric.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index 24f0d4a8..92416d4c 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -31,7 +31,7 @@ Note: Nodes $0, 107, 348, 414, 686, 698, 1684, 1912, 3437, 3980$ are the ones wh +++ -### Import packages +## Import packages ```{code-cell} ipython3 import pandas as pd diff --git a/content/generators/geometric.md b/content/generators/geometric.md index 263ad8d9..bcca2b40 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -28,7 +28,7 @@ In this tutorial, we'll explore the geometric network generator models implemented under [`networkx/generators/geometric.py`](https://github.com/networkx/networkx/blob/main/networkx/generators/geometric.py) and apply them to a real-world use case to learn how these models can be parameterized and used. -### Import packages +## Import packages ```{code-cell} ipython3 %matplotlib inline From 64eaf96cfa0815aac0a1bdcfb1d3e340730cca46 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:49:47 -0400 Subject: [PATCH 18/36] Resolved suggestions --- .../algorithms/assortativity/correlation.md | 3 +-- content/algorithms/dag/index.md | 10 +------ content/algorithms/euler/euler.md | 8 +++--- content/algorithms/flow/dinitz_alg.md | 4 +-- content/algorithms/lca/LCA.md | 26 ++++++++++++------- .../facebook_notebook.md | 2 -- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index c1fed531..7ed52471 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -24,7 +24,7 @@ language_info: # Node Assortativity Coefficients and Correlation Measures -In this tutorial, we will explore the theory of [assortativity](https://en.wikipedia.org/wiki/Assortativity) [^1] and its measures. +In this tutorial, we will explore the theory of assortativity [^1] and its measures. We'll focus on assortativity measures available in NetworkX at [`algorithms/assortativity/correlation.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/assortativity/correlation.py): * Attribute assortativity @@ -151,7 +151,6 @@ Illustrating how value of assortativity changes ```{code-cell} ipython3 gname = "g2" -# loading the graph G = nx.read_graphml(f"data/{gname}.graphml") with open(f"data/pos_{gname}", "rb") as fp: pos = pickle.load(fp) diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 9473dae9..83f25e60 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -42,12 +42,7 @@ import inspect ## Example: Directed Graphs ```{code-cell} ipython3 -# Create graph triangle_graph = nx.DiGraph([(1, 2), (2, 3), (3, 1)]) -``` - -```{code-cell} ipython3 -# Draw graph nx.draw_planar( triangle_graph, with_labels=True, @@ -74,14 +69,11 @@ You will see this idea in action in the examples below. ### Example ```{code-cell} ipython3 -# Read in from GraphML file clothing_graph = nx.read_graphml(f"data/clothing_graph.graphml") ``` ```{code-cell} ipython3 plt.figure(figsize=(12, 12), dpi=150) - -# Draw graph nx.draw_planar( clothing_graph, arrowsize=12, @@ -170,7 +162,7 @@ Then, a topological sort gives an order in which to perform the jobs. A closely related application of topological sorting algorithms was first studied in the early 1960s in the context of the -[PERT technique](https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique) [^1] +PERT technique [^1] for scheduling in project management. In this application, the vertices of a graph represent the milestones of a project, and the edges represent tasks that must be performed between one milestone and another. diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index d67de25e..267fadf2 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -16,7 +16,7 @@ kernelspec: +++ -In this tutorial, we will explore the Euler's algorithm and its implementation in NetworkX under [`networkx/algorithms/euler.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/euler.py). +In this tutorial, we will explore Euler's algorithm and its implementation in NetworkX under [`networkx/algorithms/euler.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/euler.py). ## Import package @@ -128,7 +128,7 @@ Euler generalized the method he applied for Königsberg problem as follows: - If there are two vertices with odd degree, then they are the starting and ending vertices. - If there are no vertices with odd degree, any vertex can be starting or ending vertex and the graph has also an Euler Circuit. -## Implementations of Euler's Algorithm +## Euler's Algorithm in NetworkX NetworkX implements several methods using the Euler's algorithm. These are: - **is_eulerian** : Whether the graph has an Eulerian circuit @@ -295,6 +295,6 @@ Euler's algorithm is essential for anyone or anything that uses paths. Some exam ## References -1. [Wikipedia, Seven Bridge of Konigsberg](https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg) +[^1]: [Wikipedia, Seven Bridge of Konigsberg](https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg) -2. Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. +[^2]: Euler, Leonhard, ‘Solutio problematis ad geometriam situs pertinentis’ (1741), Eneström 53, MAA Euler Archive. diff --git a/content/algorithms/flow/dinitz_alg.md b/content/algorithms/flow/dinitz_alg.md index de3a5d41..0cbb40d7 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -23,8 +23,7 @@ language_info: --- # Dinitz's Algorithm and Applications -In this tutorial, we will explore the [maximum flow problem](https://en.wikipedia.org/wiki/Maximum_flow_problem) [^1] -and [Dinitz's algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm) [^2], which is implemented at +In this tutorial, we will explore the maximum flow problem [^2], which is implemented at [`algorithms/flow/dinitz_alg.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/flow/dinitz_alg.py) in NetworkX. We will also see how it can be used to solve some interesting problems. @@ -52,7 +51,6 @@ along the paths you specify with certain limits on the sizes of data per second you can send between a pair of nodes in this network. ```{code-cell} ipython3 -# Load the example graph G = nx.read_gml("data/example_graph.gml") # Extract info about node position from graph (for visualization) diff --git a/content/algorithms/lca/LCA.md b/content/algorithms/lca/LCA.md index 45d5c363..fc5f55dc 100644 --- a/content/algorithms/lca/LCA.md +++ b/content/algorithms/lca/LCA.md @@ -15,7 +15,18 @@ kernelspec: # Lowest Common Ancestor -In this tutorial, we will explore the python implementation of the lowest common ancestor algorithm in NetworkX at `networkx/algorithms/lowest_common_ancestor.py`. To get a more general overview of Lowest Common Ancestor you can also read the [wikipedia article](https://en.wikipedia.org/wiki/Lowest_common_ancestor). This notebook expects readers to be familiar with the NetworkX API. If you are new to NetworkX, you can go through the [introductory tutorial](https://networkx.org/documentation/latest/tutorial.html). +In this tutorial, we will explore the python implementation of the lowest common ancestor algorithm [^1] in NetworkX at [`networkx/algorithms/lowest_common_ancestor.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/lowest_common_ancestors.py). This notebook expects readers to be familiar with the NetworkX API. If you are new to NetworkX, you can go through the [introductory tutorial](https://networkx.org/documentation/latest/tutorial.html). + +## Import packages + +```{code-cell} ipython3 +import matplotlib.pyplot as plt +import networkx as nx +from networkx.drawing.nx_agraph import graphviz_layout +from itertools import chain, count, combinations_with_replacement +``` + ++++ {"id": "Z5VJ4S_mlMiI"} ## Definitions @@ -28,7 +39,6 @@ Before diving into the algorithm, let's first remember the concepts of an ancest - **Lowest Common Ancestor:** For two of nodes $u$ and $v$ in a tree, the lowest common ancestor is the lowest (i.e. deepest) node which is an ancestor of both $u$ and $v$. -+++ {"id": "Z5VJ4S_mlMiI"} ## Example @@ -40,13 +50,6 @@ It is always a good idea to learn concepts with an example. Consider the followi Let's first draw the tree using NetworkX. -```{code-cell} -import matplotlib.pyplot as plt -import networkx as nx -from networkx.drawing.nx_agraph import graphviz_layout -from itertools import chain, count, combinations_with_replacement -``` - ```{code-cell} T = nx.DiGraph() T.add_edges_from( @@ -224,3 +227,8 @@ dict(nx.all_pairs_lowest_common_ancestor(G)) Naive implementation of lowest common ancestor algorithm finds all ancestors of all nodes in the given pairs. Let the number of nodes given in the pairs be P. In the worst case, finding ancestors of a single node will take O(|V|) times where |V| is the number of nodes. Thus, constructing the ancestor cache of a graph will take O(|V|\*P) times. This step will dominate the others and determine the worst-case running time of the algorithm. The space complexity of the algorithm will also be determined by the ancestor cache. For each node in the given pairs, there might be O(|V|) ancestors. Thus, space complexity is also O(|V|\*P). + ++++ + +## References +[^1]: [Wikipedia, Lowest common ancestor](https://en.wikipedia.org/wiki/Lowest_common_ancestor) diff --git a/content/exploratory_notebooks/facebook_notebook.md b/content/exploratory_notebooks/facebook_notebook.md index 92416d4c..ce461f4e 100644 --- a/content/exploratory_notebooks/facebook_notebook.md +++ b/content/exploratory_notebooks/facebook_notebook.md @@ -47,7 +47,6 @@ from random import randint The edges are loaded from the `data` folder and saved in a dataframe. Each edge is a new row and for each edge there is a `start_node` and an `end_node` column ```{code-cell} ipython3 -# Read in data facebook = pd.read_csv( "data/facebook_combined.txt.gz", compression="gzip", @@ -315,7 +314,6 @@ Looking at the results, the node $107$ has a betweenness centrality of $0.48$, m Moving on, the distribution of betweenness centralities will be plotted: ```{code-cell} ipython3 -# Set up figure plt.figure(figsize=(15, 8)) plt.hist(betweenness_centrality.values(), bins=100) plt.xticks(ticks=[0, 0.02, 0.1, 0.2, 0.3, 0.4, 0.5]) # set the x axis ticks From f2c7877aa03cb2be8418e2674c5c6ea48e4a518e Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:33:47 -0400 Subject: [PATCH 19/36] created traversal notebook draft 1 --- content/algorithms/traversal/index.md | 484 ++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 content/algorithms/traversal/index.md diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md new file mode 100644 index 00000000..9ab65a10 --- /dev/null +++ b/content/algorithms/traversal/index.md @@ -0,0 +1,484 @@ +--- +jupytext: + main_language: python + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.14.5 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Graph Traversal Algorithms + ++++ + +In this guide, we will explore graph traversal algorithms implemented under [networkx/algorithms/traversal.py](https://networkx.org/documentation/stable/reference/algorithms/traversal.html), mainly breadth-first search (BFS) and depth-first search (DFS). + ++++ + +## Import packages + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +from IPython.display import clear_output, display +import time +``` + +## Introduction + +A graph traversal is also known as a graph search. + +There are many scenarios in which searching a graph is of interest. Consider, for example, you are at one node and want to search for another. You could also be interested in starting from one node and travelling to all other nodes to check some property or to do some operation. + + +A concrete example is below. In the below graph, starting from the green vertex is there some way to get to the red? This is can example of a search problem. + +```{code-cell} ipython3 +G = nx.Graph() +G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9)]) +pos = nx.spectral_layout(G) +node_colors = ['green' if node == 2 else 'red' if node == 9 else 'blue' for node in G.nodes] +nx.draw(G, pos, with_labels=True, font_color='white', node_color=node_colors, node_size=500, font_size=10, font_weight='bold') +plt.show() +``` + +Given this, what procedure should we use to search/traverse a graph? There are 2 major ways to do this: BFS and DFS. + +## BFS + ++++ + +Consider the problem from above. Starting from node 2 in green, how do we search for node 9 in red? + ++++ + +One way to go about it is as follows: from a start node, check all neighbors of that node. Repeat for each neighbor (i.e. checking all of its neighbors), and on and on. In the process, ensure you only visit nodes that have not already been visited. Intuitively, you are searching in a 'breadth-first' fashion: checking all the neighbors of vertices (surveying all your options) without committing to a single 'branch' and deep diving into it. See the pseudocode below: + ++++ + +``` +BFS(G, start): + let Q be a queue + label start as explored + Q.enqueue(start) + while Q is not empty do + v := Q.dequeue() + if v is the goal then + return v + for all edges from v to w in G.adjacentEdges(v) do + if w is not labeled as explored then + label w as explored + w.parent := v + Q.enqueue(w) + ``` + ++++ + +Note the use of a queue...its first-in first-out (FIFO) nature enables remembering the next vertex to start a search at. We also mark vertices as explored to ensure we do not visit them again. + +Here is an example animation for BFS: each explored 'layer' is marked with a different color. Notice how we check all neighbors for each vertex, with committing to a single path. + +Do not worry about the code here, we will explore networkx's implementations further later + +```{code-cell} ipython3 +def animate_bfs(G, start_node): + pos = nx.spring_layout(G) + + fig, ax = plt.subplots(figsize=(12, 9)) + + node_colors = {node: 'skyblue' for node in G.nodes} + + visited = set() + queue = [start_node] + layers = list(nx.bfs_layers(G, start_node)) + + for layer_idx, layer in enumerate(layers): + next_layer = [] + + for node in layer: + if node not in visited: + visited.add(node) + node_colors[node] = mcolors.hsv_to_rgb((layer_idx / len(layers), 0.7, 0.9)) + next_layer.extend(G.neighbors(node)) + + nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], + node_size=1000, font_size=10, font_weight='bold', ax=ax) + + clear_output(wait=True) + display(fig) + time.sleep(1) + + queue = next_layer + + clear_output(wait=True) + nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], + node_size=1000, font_size=10, font_weight='bold') + plt.show() + +# Example usage with a larger graph +G = nx.Graph() +G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), + (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), + (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) + +# Start the BFS animation from the root node (node 0) +animate_bfs(G, start_node=0) +``` + +## Using NetworkX for BFS + +Now that we understand BFS, what functionality does NetworkX provide for BFS? + +We have six use cases. +- bfs_layers +- bfs_edges +- bfs_tree +- bfs_predecessors +- bfs_successors +- descendants_at_distance + + +bfs_layers returns an iterator of all the 'layers' of a BFS: that is all the vertices that are on the same level (i.e. distance) from the root. BFS operates layer by layer. We use bfs_layers in the above to color the nodes at each layer + +We can bfs_edges to explicitly show all the edges that are traversed in the above graph during a BFS. Check it out below. It matches our animation! + +```{code-cell} ipython3 +bfs_edges_result = list(nx.bfs_edges(G, 0)) +print("Edges traversed during BFS traversal:") +for edge in bfs_edges_result: + print(edge) +``` + +bfs_tree creates a "traversal" tree that shows what vertices were traversed in what order. It is a representation of the BFS, and must always be a tree because BFS visits no vertex twice (we ensure that doesn't happen by keeping track of what vertices are explored in the traversal) + +```{code-cell} ipython3 +start_node = 0 +bfs_tree = nx.bfs_tree(G, start_node) + +# Draw the BFS tree +pos = nx.spring_layout(G) # Layout the original graph nodes using spring layout +plt.figure(figsize=(12, 9)) +nx.draw(bfs_tree, pos, with_labels=True, node_size=1000, font_size=10, font_weight='bold', alpha=0.6, node_color='skyblue', width=2) + +# Draw the BFS tree edges with a different color and width to distinguish it from the original graph + +plt.title("BFS Tree") +plt.show() +``` + +bfs_successor and bfs_predecessor both output lists of nodes and their sucessors and nodes and their predecessors, respectively. Below, I use these functions to reconstruct the graph. As you can see, in the graph on the left all edges are from nodes to their predecessors (and thus we have the BFS tree but all edges reversed). In the graph on the right all edges from nodes to their successors (and thus we have the BFS tree) + + +Finally, we have descendants_at_distance, which is a useful way of identifying all nodes at a fixed distance from a source in G. It uses BFS (remember, layer number is the same as distance from the root) to do so. In our example graph, supposed I am at node 9 and want to find all nodes 2 edges away, because I can only travel two edges. I do so as below! + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt + +import networkx as nx +import matplotlib.pyplot as plt + +# Create the graph G +G = nx.Graph() +G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), + (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), + (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) + +# Find vertices at distance 2 from vertex 0 +vertices_at_distance_2 = nx.descendants_at_distance(G, source=0, distance=2) + +# Draw the graph G +plt.figure(figsize=(10, 6)) +pos = nx.spring_layout(G, seed=42) + +# Draw all vertices in blue +nx.draw_networkx_nodes(G, pos, node_size=500, node_color='blue', alpha=0.6) + +# Draw edges +nx.draw_networkx_edges(G, pos, edge_color='gray', alpha=0.6) + +# Draw labels +nx.draw_networkx_labels(G, pos, font_size=10, font_weight='bold') + +# Highlight vertices at distance 2 in red +nx.draw_networkx_nodes(G, pos, nodelist=vertices_at_distance_2, node_size=500, node_color='red', alpha=0.8) + +plt.title("Graph G with Vertices at Distance 2 from Vertex 0 Highlighted in Red") +plt.axis('off') +plt.show() + + +``` + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt + +# Create the graph G +G = nx.Graph() +G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), + (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), + (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) + +# Perform BFS starting from node 0 +start_node = 0 +predecessors = dict(nx.bfs_predecessors(G, start_node)) +successors = dict(nx.bfs_successors(G, start_node)) + +# Create a new graph for the BFS tree with predecessor edges +BFS_predecessor_tree = nx.DiGraph() + +# Add the nodes from G to BFS_predecessor_tree +BFS_predecessor_tree.add_nodes_from(G.nodes) + +# Add predecessor edges +for node, pred in predecessors.items(): + if pred is not None: + BFS_predecessor_tree.add_edge(node, pred, directed=True) + +# Create a new graph for the BFS tree with successor edges +BFS_successor_tree = nx.DiGraph() + +# Add the nodes from G to BFS_successor_tree +BFS_successor_tree.add_nodes_from(G.nodes) + +# Add successor edges +for node, succ in successors.items(): + for s in succ: + BFS_successor_tree.add_edge(node, s, directed=True) + +# Draw G with predecessor edges +plt.figure(figsize=(15, 4)) +plt.subplot(1, 2, 1) +pos = nx.spring_layout(G, seed=42) +nx.draw_networkx(BFS_predecessor_tree, pos, with_labels=True, node_size=150, arrows=True, node_color='skyblue', edge_color='blue', width=2, arrowstyle='->', arrowsize=9) +plt.title("Graph G with Predecessor Edges") +plt.axis('off') + +# Draw G with successor edges +plt.subplot(1, 2, 2) +nx.draw_networkx(BFS_successor_tree, pos, with_labels=True, node_size=150, arrows=True, node_color='skyblue', edge_color='red', width=2, arrowstyle='->', arrowsize=9) +plt.title("Graph G with Successor Edges") +plt.axis('off') + +plt.tight_layout() +plt.show() +``` + +## DFS + +One way to approach it is as follows: from a start node, explore one of its neighbors and continue exploring further down that branch as deep as possible before backtracking. Repeat this process for each unvisited neighbor, going deeper into each branch before returning to explore other unvisited options. In this manner, you are searching in a 'depth-first' fashion, delving deeply into one branch at a time. See the pseudocode below: + ++++ + +``` +// ITERATIVE APPROACH + dfs-iterative (G, src): //Where G is graph and src is source vertex + let st be stack + st.push(src) //Inserting src in stack + mark s as visited + while (st is not empty): + //Pop a vertex from stack to visit next + v = st.top( ) + st.pop( ) + //Push all the neighbours of v in stack that are not visited + for all neighbours w of v in Graph G: + if w is not visited : + st.push(w) + mark w as visited + + +// RECURSIVE APPROACH + dfs-recursive(G, src): + mark src as visited + for all neighbours w of src in Graph G: + if w is not visited: + DFS-recursive(G, w) +``` + ++++ + +The iterative approach uses an explicit stack to keep track of vertices. It starts by pushing the source vertex onto the stack and marking it as visited. While the stack is not empty, the algorithm pops a vertex from the top of the stack, processes it, and then pushes its unvisited neighbors onto the stack. This process continues until the stack is empty, ensuring deep exploration along each branch. + +On the other hand, in the recursive approach the algorithm starts with a source vertex. It marks the vertex as visited and then recursively calls itself for each unvisited neighbor. This recursive process allows the algorithm to explore each branch deeply before backtracking to explore other neighbors. + + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +from IPython.display import clear_output, display +import time + +def animate_dfs(G, start_node): + pos = nx.spring_layout(G) + + fig, ax = plt.subplots(figsize=(12, 9)) + + node_colors = {node: 'skyblue' for node in G.nodes} + + visited = set() + + def dfs(node): + visited.add(node) + node_colors[node] = 'yellow' + nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], + node_size=1000, font_size=10, font_weight='bold', ax=ax) + + clear_output(wait=True) + display(fig) + time.sleep(1) + + for neighbor in G.neighbors(node): + if neighbor not in visited: + dfs(neighbor) + + dfs(start_node) + + clear_output(wait=True) + nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], + node_size=1000, font_size=10, font_weight='bold') + plt.show() + +# Example usage with the same larger graph +G = nx.Graph() +G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), + (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), + (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) + +# Start the DFS animation from the root node (node 0) +animate_dfs(G, start_node=0) +``` + +Note how we dive deeply into a branch before moving on to another branch, instead of moving out in levels like BFS + ++++ + +## Using NetworkX for DFS + +Now that we understand DFS, what functionality does NetworkX provide for DFS? + +We have six use cases. +- dfs_edges +- dfs_tree +- dfs_predecessors +- dfs_successors +- dfs_labelled_edges +- dfs_preorder_nodes +- dfs_postorder_nodes +- descendants_at_distance + +dfs_predecessors and dfs_successors do essentially the same as their BFS counterparts but with BFS, so we'll ignore those for now. Likewise, dfs_tree simply builds a tree corresponding to the DFS traversal and descendants_at_distance does the same as its BFS counterpart, just using DFS as a base to traverse. + +As such, we will focus on the remaining: dfs_edges, dfs_labelled_edges, and the preorder and postorder node functions. + +We can dfs_edges to explicitly show all the edges that are traversed in the above graph during a DFS. Check it out below. It matches our animation as well! + +```{code-cell} ipython3 +dfs_edges_result = list(nx.dfs_edges(G, 0)) +print("Edges traversed during BFS traversal:") +for edge in dfs_edges_result: + print(edge) +``` + +```{code-cell} ipython3 +G = nx.DiGraph([(0, 1), (1, 2), (2, 1)]) +# Label the edges using dfs_labeled_edges + +edge_labels = {(u, v): d for u, v, d in nx.dfs_labeled_edges(G, source=0)} + +print(list(nx.dfs_labeled_edges(G, source=0))) + +# Draw the graph with labeled edges +pos = nx.random_layout(G, seed=42) + +plt.figure(figsize=(8, 6)) +nx.draw(G, pos, with_labels=True, node_size=1000, node_color='skyblue', arrowsize=10, font_size=10, font_weight='bold') +nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8, font_weight='bold') +plt.title("Example Graph with Labeled Edges using DFS") +plt.axis('off') +plt.show() +``` + +As per the documentation, a ‘forward’ edge is one in which u has been visited but v has not. A ‘nontree’ edge is one in which both u and v have been visited but the edge is not in the DFS tree. A ‘reverse’ edge is one in which both u and v have been visited and the edge is in the DFS tree. + +Note the reverse and nontree edges in the above illustrative example. + + +Finally, can take a look at dfs_postorder and dfs_preorder. + +```{code-cell} ipython3 +import networkx as nx +import matplotlib.pyplot as plt + +# Create a simple graph +G = nx.DiGraph() +G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (1, 6)]) # Adding vertex 6 and connecting to vertex 1 + +# Draw the graph on the left with DFS postorder node labels +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) + +postorder_nodes = list(nx.dfs_postorder_nodes(G, source=1)) +pos1 = nx.spectral_layout(G) +nx.draw(G, pos1, with_labels=True, node_color='skyblue', node_size=1000, font_size=0, font_weight='bold', + ax=ax1, arrows=True) +ax1.set_title("Graph with DFS Postorder Node Labels") + +# Draw the graph on the right with DFS preorder node labels +preorder_nodes = list(nx.dfs_preorder_nodes(G, source=1)) +pos2 = nx.spectral_layout(G) +nx.draw(G, pos2, with_labels=True, node_color='skyblue', node_size=1000, font_size=0, font_weight='bold', + ax=ax2, arrows=True) +ax2.set_title("Graph with DFS Preorder Node Labels") + +# Label nodes with DFS postorder and preorder numbers +node_labels_postorder = {node: f"{i}" for i, node in enumerate(postorder_nodes, 1)} +node_labels_preorder = {node: f"{i}" for i, node in enumerate(preorder_nodes, 1)} + +nx.draw_networkx_labels(G, pos1, labels=node_labels_postorder, font_size=10, font_color='red', font_weight='bold', ax=ax1) +nx.draw_networkx_labels(G, pos2, labels=node_labels_preorder, font_size=10, font_color='red', font_weight='bold', ax=ax2) + +plt.tight_layout() +plt.show() +``` + +DFS postorder is an ordering in which we visit a node's children first before processing the node itself. Starting from the source node, we traverse deeper into the graph, visiting all the child nodes before processing the current node. Once all the child nodes are visited, we backtrack and process the current node. This order is often used to compute the size of subtrees and perform tasks that depend on information from the children. + +On the other hand, DFS preorder is an ordering in which we process the current node before visiting its children. We start from the source node, process it, then move to its first child, process it, and so on, until all children are processed. Only then do we backtrack and continue processing other children of the parent node. This order is useful for tasks that require processing nodes before their children, such as copying the entire tree structure. + ++++ + +## Applications of BFS and DFS + ++++ + +Breadth-First Search (BFS) and Depth-First Search (DFS) are fundamental graph traversal algorithms that find numerous applications in computer science and various real-world scenarios. + +BFS is commonly used for finding the shortest path between two nodes in an unweighted graph. It systematically explores the graph layer by layer, guaranteeing that the first path discovered is the shortest one. Beyond path finding, BFS also helps analyze the connectivity of a graph. By starting from a source node and visiting all reachable nodes, BFS can determine whether the graph is connected or contains isolated components. + +In networking, BFS plays a crucial role in routing protocols. By finding the shortest path between devices on a network, BFS ensures efficient data transmission with minimal delays and hops. Web crawling is another application of BFS, where it facilitates systematic exploration of web pages, beginning from a specific webpage and gradually discovering linked pages layer by layer. + +Conversely, DFS is highly efficient for exploring all nodes in a graph. It excels in various graph traversal tasks, enabling the identification of connected components, cycles, and other structural properties. DFS is particularly valuable in topological sorting of directed acyclic graphs (DAGs). The algorithm orders the nodes in a manner that preserves dependencies, making it widely used in tasks like scheduling and project planning. + +DFS finds its place in solving mazes and puzzles as well. By exploring different possible paths systematically, DFS can find solutions to puzzles where the goal is to reach a specific destination. Additionally, DFS is utilized in detecting cycles in a graph, aiding in various applications such as deadlock detection in operating systems and identifying circular dependencies in software design. + + +## References + +[^1]: Depth-First Search (DFS). (n.d.). University of Toronto - Computer Science. Retrieved from [http://www.cs.toronto.edu/~heap/270F02/node36.html](http://www.cs.toronto.edu/~heap/270F02/node36.html) + +[^2]: Applications of Depth-First and Breadth-First Search. (n.d.). York University - Department of Electrical Engineering and Computer Science. Retrieved from [https://wiki.eecs.yorku.ca/course_archive/2012-13/S/2011/_media/22Apps.pdf](https://wiki.eecs.yorku.ca/course_archive/2012-13/S/2011/_media/22Apps.pdf) + +[^3]: Depth-first search. (2021, September 16). In Wikipedia, The Free Encyclopedia. Retrieved July 30, 2023, from [https://en.wikipedia.org/wiki/Depth-first_search](https://en.wikipedia.org/wiki/Depth-first_search) + +[^4]: Breadth-first search. (2021, September 17). In Wikipedia, The Free Encyclopedia. Retrieved July 30, 2023, from [https://en.wikipedia.org/wiki/Breadth-first_search](https://en.wikipedia.org/wiki/Breadth-first_search) + +[^5]: NetworkX. (2021, August 29). Traversal — NetworkX documentation. Retrieved July 30, 2023, from [https://networkx.org/documentation/stable/reference/traversal.html](https://networkx.org/documentation/stable/reference/traversal.html) + +[^6]: Wikipedia. Lowest common ancestor. [https://en.wikipedia.org/wiki/Lowest_common_ancestor](https://en.wikipedia.org/wiki/Lowest_common_ancestor) From 3fdf8b5fc716288818a33f0c9c5f743c07e95c82 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:37:07 -0400 Subject: [PATCH 20/36] added to index and to requirements --- content/algorithms/index.md | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/content/algorithms/index.md b/content/algorithms/index.md index a9a2b7b1..ff75b820 100644 --- a/content/algorithms/index.md +++ b/content/algorithms/index.md @@ -13,4 +13,5 @@ flow/dinitz_alg lca/LCA euler/euler isomorphism/isomorphism +traversal/index ``` diff --git a/requirements.txt b/requirements.txt index 72ea401c..3c578558 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ jupytext # Notebook requirements pygraphviz +time From c1f21e26eceddfa395ccd9f755e323e578b05e71 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:44:00 -0400 Subject: [PATCH 21/36] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3c578558..454fcd18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,4 @@ jupytext # Notebook requirements pygraphviz -time + From e0c88e8b74037adcba649663740040c94db47de0 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Sat, 19 Aug 2023 12:02:20 -0400 Subject: [PATCH 22/36] fix formatting --- content/algorithms/traversal/index.md | 338 ++++++++++++++++++++++---- 1 file changed, 286 insertions(+), 52 deletions(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index 9ab65a10..f8dffee0 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -5,7 +5,7 @@ jupytext: extension: .md format_name: myst format_version: 0.13 - jupytext_version: 1.14.5 + jupytext_version: 1.15.0 kernelspec: display_name: Python 3 language: python @@ -41,10 +41,23 @@ A concrete example is below. In the below graph, starting from the green vertex ```{code-cell} ipython3 G = nx.Graph() -G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9)]) +G.add_edges_from( + [(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9)] +) pos = nx.spectral_layout(G) -node_colors = ['green' if node == 2 else 'red' if node == 9 else 'blue' for node in G.nodes] -nx.draw(G, pos, with_labels=True, font_color='white', node_color=node_colors, node_size=500, font_size=10, font_weight='bold') +node_colors = [ + "green" if node == 2 else "red" if node == 9 else "blue" for node in G.nodes +] +nx.draw( + G, + pos, + with_labels=True, + font_color="white", + node_color=node_colors, + node_size=500, + font_size=10, + font_weight="bold", +) plt.show() ``` @@ -92,7 +105,7 @@ def animate_bfs(G, start_node): fig, ax = plt.subplots(figsize=(12, 9)) - node_colors = {node: 'skyblue' for node in G.nodes} + node_colors = {node: "skyblue" for node in G.nodes} visited = set() queue = [start_node] @@ -104,11 +117,21 @@ def animate_bfs(G, start_node): for node in layer: if node not in visited: visited.add(node) - node_colors[node] = mcolors.hsv_to_rgb((layer_idx / len(layers), 0.7, 0.9)) + node_colors[node] = mcolors.hsv_to_rgb( + (layer_idx / len(layers), 0.7, 0.9) + ) next_layer.extend(G.neighbors(node)) - nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], - node_size=1000, font_size=10, font_weight='bold', ax=ax) + nx.draw( + G, + pos, + with_labels=True, + node_color=[node_colors[node] for node in G.nodes], + node_size=1000, + font_size=10, + font_weight="bold", + ax=ax, + ) clear_output(wait=True) display(fig) @@ -117,15 +140,50 @@ def animate_bfs(G, start_node): queue = next_layer clear_output(wait=True) - nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], - node_size=1000, font_size=10, font_weight='bold') + nx.draw( + G, + pos, + with_labels=True, + node_color=[node_colors[node] for node in G.nodes], + node_size=1000, + font_size=10, + font_weight="bold", + ) plt.show() + # Example usage with a larger graph G = nx.Graph() -G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), - (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), - (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) +G.add_edges_from( + [ + (0, 1), + (0, 2), + (0, 3), + (1, 4), + (1, 5), + (3, 6), + (3, 7), + (4, 8), + (7, 8), + (8, 9), + (2, 10), + (2, 11), + (10, 12), + (10, 13), + (12, 14), + (12, 15), + (5, 16), + (5, 17), + (16, 18), + (16, 19), + (18, 20), + (18, 21), + (7, 22), + (7, 23), + (22, 24), + (22, 25), + ] +) # Start the BFS animation from the root node (node 0) animate_bfs(G, start_node=0) @@ -164,7 +222,17 @@ bfs_tree = nx.bfs_tree(G, start_node) # Draw the BFS tree pos = nx.spring_layout(G) # Layout the original graph nodes using spring layout plt.figure(figsize=(12, 9)) -nx.draw(bfs_tree, pos, with_labels=True, node_size=1000, font_size=10, font_weight='bold', alpha=0.6, node_color='skyblue', width=2) +nx.draw( + bfs_tree, + pos, + with_labels=True, + node_size=1000, + font_size=10, + font_weight="bold", + alpha=0.6, + node_color="skyblue", + width=2, +) # Draw the BFS tree edges with a different color and width to distinguish it from the original graph @@ -186,9 +254,36 @@ import matplotlib.pyplot as plt # Create the graph G G = nx.Graph() -G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), - (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), - (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) +G.add_edges_from( + [ + (0, 1), + (0, 2), + (0, 3), + (1, 4), + (1, 5), + (3, 6), + (3, 7), + (4, 8), + (7, 8), + (8, 9), + (2, 10), + (2, 11), + (10, 12), + (10, 13), + (12, 14), + (12, 15), + (5, 16), + (5, 17), + (16, 18), + (16, 19), + (18, 20), + (18, 21), + (7, 22), + (7, 23), + (22, 24), + (22, 25), + ] +) # Find vertices at distance 2 from vertex 0 vertices_at_distance_2 = nx.descendants_at_distance(G, source=0, distance=2) @@ -198,22 +293,22 @@ plt.figure(figsize=(10, 6)) pos = nx.spring_layout(G, seed=42) # Draw all vertices in blue -nx.draw_networkx_nodes(G, pos, node_size=500, node_color='blue', alpha=0.6) +nx.draw_networkx_nodes(G, pos, node_size=500, node_color="blue", alpha=0.6) # Draw edges -nx.draw_networkx_edges(G, pos, edge_color='gray', alpha=0.6) +nx.draw_networkx_edges(G, pos, edge_color="gray", alpha=0.6) # Draw labels -nx.draw_networkx_labels(G, pos, font_size=10, font_weight='bold') +nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold") # Highlight vertices at distance 2 in red -nx.draw_networkx_nodes(G, pos, nodelist=vertices_at_distance_2, node_size=500, node_color='red', alpha=0.8) +nx.draw_networkx_nodes( + G, pos, nodelist=vertices_at_distance_2, node_size=500, node_color="red", alpha=0.8 +) plt.title("Graph G with Vertices at Distance 2 from Vertex 0 Highlighted in Red") -plt.axis('off') +plt.axis("off") plt.show() - - ``` ```{code-cell} ipython3 @@ -222,9 +317,36 @@ import matplotlib.pyplot as plt # Create the graph G G = nx.Graph() -G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), - (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), - (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) +G.add_edges_from( + [ + (0, 1), + (0, 2), + (0, 3), + (1, 4), + (1, 5), + (3, 6), + (3, 7), + (4, 8), + (7, 8), + (8, 9), + (2, 10), + (2, 11), + (10, 12), + (10, 13), + (12, 14), + (12, 15), + (5, 16), + (5, 17), + (16, 18), + (16, 19), + (18, 20), + (18, 21), + (7, 22), + (7, 23), + (22, 24), + (22, 25), + ] +) # Perform BFS starting from node 0 start_node = 0 @@ -257,15 +379,37 @@ for node, succ in successors.items(): plt.figure(figsize=(15, 4)) plt.subplot(1, 2, 1) pos = nx.spring_layout(G, seed=42) -nx.draw_networkx(BFS_predecessor_tree, pos, with_labels=True, node_size=150, arrows=True, node_color='skyblue', edge_color='blue', width=2, arrowstyle='->', arrowsize=9) +nx.draw_networkx( + BFS_predecessor_tree, + pos, + with_labels=True, + node_size=150, + arrows=True, + node_color="skyblue", + edge_color="blue", + width=2, + arrowstyle="->", + arrowsize=9, +) plt.title("Graph G with Predecessor Edges") -plt.axis('off') +plt.axis("off") # Draw G with successor edges plt.subplot(1, 2, 2) -nx.draw_networkx(BFS_successor_tree, pos, with_labels=True, node_size=150, arrows=True, node_color='skyblue', edge_color='red', width=2, arrowstyle='->', arrowsize=9) +nx.draw_networkx( + BFS_successor_tree, + pos, + with_labels=True, + node_size=150, + arrows=True, + node_color="skyblue", + edge_color="red", + width=2, + arrowstyle="->", + arrowsize=9, +) plt.title("Graph G with Successor Edges") -plt.axis('off') +plt.axis("off") plt.tight_layout() plt.show() @@ -308,7 +452,6 @@ The iterative approach uses an explicit stack to keep track of vertices. It star On the other hand, in the recursive approach the algorithm starts with a source vertex. It marks the vertex as visited and then recursively calls itself for each unvisited neighbor. This recursive process allows the algorithm to explore each branch deeply before backtracking to explore other neighbors. - ```{code-cell} ipython3 import networkx as nx import matplotlib.pyplot as plt @@ -316,20 +459,29 @@ import matplotlib.colors as mcolors from IPython.display import clear_output, display import time + def animate_dfs(G, start_node): pos = nx.spring_layout(G) fig, ax = plt.subplots(figsize=(12, 9)) - node_colors = {node: 'skyblue' for node in G.nodes} + node_colors = {node: "skyblue" for node in G.nodes} visited = set() def dfs(node): visited.add(node) - node_colors[node] = 'yellow' - nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], - node_size=1000, font_size=10, font_weight='bold', ax=ax) + node_colors[node] = "yellow" + nx.draw( + G, + pos, + with_labels=True, + node_color=[node_colors[node] for node in G.nodes], + node_size=1000, + font_size=10, + font_weight="bold", + ax=ax, + ) clear_output(wait=True) display(fig) @@ -342,15 +494,50 @@ def animate_dfs(G, start_node): dfs(start_node) clear_output(wait=True) - nx.draw(G, pos, with_labels=True, node_color=[node_colors[node] for node in G.nodes], - node_size=1000, font_size=10, font_weight='bold') + nx.draw( + G, + pos, + with_labels=True, + node_color=[node_colors[node] for node in G.nodes], + node_size=1000, + font_size=10, + font_weight="bold", + ) plt.show() + # Example usage with the same larger graph G = nx.Graph() -G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 4), (1, 5), (3, 6), (3, 7), (4, 8), (7, 8), (8, 9), - (2, 10), (2, 11), (10, 12), (10, 13), (12, 14), (12, 15), (5, 16), (5, 17), - (16, 18), (16, 19), (18, 20), (18, 21), (7, 22), (7, 23), (22, 24), (22, 25)]) +G.add_edges_from( + [ + (0, 1), + (0, 2), + (0, 3), + (1, 4), + (1, 5), + (3, 6), + (3, 7), + (4, 8), + (7, 8), + (8, 9), + (2, 10), + (2, 11), + (10, 12), + (10, 13), + (12, 14), + (12, 15), + (5, 16), + (5, 17), + (16, 18), + (16, 19), + (18, 20), + (18, 21), + (7, 22), + (7, 23), + (22, 24), + (22, 25), + ] +) # Start the DFS animation from the root node (node 0) animate_dfs(G, start_node=0) @@ -399,10 +586,21 @@ print(list(nx.dfs_labeled_edges(G, source=0))) pos = nx.random_layout(G, seed=42) plt.figure(figsize=(8, 6)) -nx.draw(G, pos, with_labels=True, node_size=1000, node_color='skyblue', arrowsize=10, font_size=10, font_weight='bold') -nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8, font_weight='bold') +nx.draw( + G, + pos, + with_labels=True, + node_size=1000, + node_color="skyblue", + arrowsize=10, + font_size=10, + font_weight="bold", +) +nx.draw_networkx_edge_labels( + G, pos, edge_labels=edge_labels, font_size=8, font_weight="bold" +) plt.title("Example Graph with Labeled Edges using DFS") -plt.axis('off') +plt.axis("off") plt.show() ``` @@ -411,7 +609,7 @@ As per the documentation, a ‘forward’ edge is one in which u has been visite Note the reverse and nontree edges in the above illustrative example. -Finally, can take a look at dfs_postorder and dfs_preorder. +Finally, can take a look at dfs_postorder and dfs_preorder. ```{code-cell} ipython3 import networkx as nx @@ -419,30 +617,66 @@ import matplotlib.pyplot as plt # Create a simple graph G = nx.DiGraph() -G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (1, 6)]) # Adding vertex 6 and connecting to vertex 1 +G.add_edges_from( + [(1, 2), (1, 3), (2, 4), (2, 5), (1, 6)] +) # Adding vertex 6 and connecting to vertex 1 # Draw the graph on the left with DFS postorder node labels fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) postorder_nodes = list(nx.dfs_postorder_nodes(G, source=1)) pos1 = nx.spectral_layout(G) -nx.draw(G, pos1, with_labels=True, node_color='skyblue', node_size=1000, font_size=0, font_weight='bold', - ax=ax1, arrows=True) +nx.draw( + G, + pos1, + with_labels=True, + node_color="skyblue", + node_size=1000, + font_size=0, + font_weight="bold", + ax=ax1, + arrows=True, +) ax1.set_title("Graph with DFS Postorder Node Labels") # Draw the graph on the right with DFS preorder node labels preorder_nodes = list(nx.dfs_preorder_nodes(G, source=1)) pos2 = nx.spectral_layout(G) -nx.draw(G, pos2, with_labels=True, node_color='skyblue', node_size=1000, font_size=0, font_weight='bold', - ax=ax2, arrows=True) +nx.draw( + G, + pos2, + with_labels=True, + node_color="skyblue", + node_size=1000, + font_size=0, + font_weight="bold", + ax=ax2, + arrows=True, +) ax2.set_title("Graph with DFS Preorder Node Labels") # Label nodes with DFS postorder and preorder numbers node_labels_postorder = {node: f"{i}" for i, node in enumerate(postorder_nodes, 1)} node_labels_preorder = {node: f"{i}" for i, node in enumerate(preorder_nodes, 1)} -nx.draw_networkx_labels(G, pos1, labels=node_labels_postorder, font_size=10, font_color='red', font_weight='bold', ax=ax1) -nx.draw_networkx_labels(G, pos2, labels=node_labels_preorder, font_size=10, font_color='red', font_weight='bold', ax=ax2) +nx.draw_networkx_labels( + G, + pos1, + labels=node_labels_postorder, + font_size=10, + font_color="red", + font_weight="bold", + ax=ax1, +) +nx.draw_networkx_labels( + G, + pos2, + labels=node_labels_preorder, + font_size=10, + font_color="red", + font_weight="bold", + ax=ax2, +) plt.tight_layout() plt.show() From fd1f94a2851e83e2105733e5a0e543a05bdb1b54 Mon Sep 17 00:00:00 2001 From: 20kav <123114628+20kavishs@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:17:43 -0400 Subject: [PATCH 23/36] Update requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 454fcd18..6fa8a033 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,8 @@ sphinx myst-nb sphinx-book-theme jupytext +black +pyupgrade # Notebook requirements pygraphviz From 0cb59a840a24a044d696a4eff94ed675a23aa243 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:37:53 -0400 Subject: [PATCH 24/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index f8dffee0..b3d45096 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -75,21 +75,23 @@ One way to go about it is as follows: from a start node, check all neighbors of +++ +```python +def BFS(G, start, goal): + """Return whether a path from start to goal exists, + Q = [start] # create a queue + seen = {start} # store the nodes as we visit them + while Q: + v = Q.pop(0) # FIFO queue + if v in seen: + continue + if v == goal: + return True + for w in G[v]: + if w not in explored: + seen.add(w) + Q.append(w) + return False ``` -BFS(G, start): - let Q be a queue - label start as explored - Q.enqueue(start) - while Q is not empty do - v := Q.dequeue() - if v is the goal then - return v - for all edges from v to w in G.adjacentEdges(v) do - if w is not labeled as explored then - label w as explored - w.parent := v - Q.enqueue(w) - ``` +++ From 82812825be65c98829361c74c4e7f241bdeb3b96 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:40:04 -0400 Subject: [PATCH 25/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index b3d45096..ddbd05f3 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -206,7 +206,7 @@ We have six use cases. bfs_layers returns an iterator of all the 'layers' of a BFS: that is all the vertices that are on the same level (i.e. distance) from the root. BFS operates layer by layer. We use bfs_layers in the above to color the nodes at each layer -We can bfs_edges to explicitly show all the edges that are traversed in the above graph during a BFS. Check it out below. It matches our animation! +We can use bfs_edges to explicitly show all the edges that are traversed in the above graph during a BFS. Check it out below. It matches our animation! ```{code-cell} ipython3 bfs_edges_result = list(nx.bfs_edges(G, 0)) From 13660425c2bb35b4e5ff749d82b9984cf4dcda4c Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:41:12 -0400 Subject: [PATCH 26/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index ddbd05f3..375cde59 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -242,7 +242,7 @@ plt.title("BFS Tree") plt.show() ``` -bfs_successor and bfs_predecessor both output lists of nodes and their sucessors and nodes and their predecessors, respectively. Below, I use these functions to reconstruct the graph. As you can see, in the graph on the left all edges are from nodes to their predecessors (and thus we have the BFS tree but all edges reversed). In the graph on the right all edges from nodes to their successors (and thus we have the BFS tree) +bfs_successor and bfs_predecessor both generate node-neighbor_list pairs where neighbors are the successors and predecessors, respectively. Below, we use these functions to reconstruct the graph. As you can see, in the graph on the left all edges are from nodes to their predecessors (and thus we have the BFS tree but all edges reversed). In the graph on the right all edges are from nodes to their successors (and thus we have the BFS tree) Finally, we have descendants_at_distance, which is a useful way of identifying all nodes at a fixed distance from a source in G. It uses BFS (remember, layer number is the same as distance from the root) to do so. In our example graph, supposed I am at node 9 and want to find all nodes 2 edges away, because I can only travel two edges. I do so as below! From ff4cd2568a722508a5821e0ad41b9d538a28f7ce Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:44:51 -0400 Subject: [PATCH 27/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index 375cde59..9d056397 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -419,7 +419,7 @@ plt.show() ## DFS -One way to approach it is as follows: from a start node, explore one of its neighbors and continue exploring further down that branch as deep as possible before backtracking. Repeat this process for each unvisited neighbor, going deeper into each branch before returning to explore other unvisited options. In this manner, you are searching in a 'depth-first' fashion, delving deeply into one branch at a time. See the pseudocode below: +Depth First Search is a way to approach Graph Traversal as follows: from a start node, explore one of its neighbors and continue exploring further down that branch as deep as possible. Then backtrack one node and repeat this process for each unvisited neighbor, going deeper into each branch before returning to explore other unvisited options. In this manner, we search in a 'depth-first' fashion, delving deeply into one branch at a time. See the pseudocode below: +++ From 73ab6c775fa50c0c9ed35826398f1f5cae6946e8 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:46:42 -0400 Subject: [PATCH 28/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index 9d056397..7ab43eda 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -563,7 +563,7 @@ We have six use cases. - dfs_postorder_nodes - descendants_at_distance -dfs_predecessors and dfs_successors do essentially the same as their BFS counterparts but with BFS, so we'll ignore those for now. Likewise, dfs_tree simply builds a tree corresponding to the DFS traversal and descendants_at_distance does the same as its BFS counterpart, just using DFS as a base to traverse. +dfs_predecessors and dfs_successors do essentially the same as their BFS counterparts but with DFS, so we'll ignore those for now. Likewise, dfs_tree simply builds a tree corresponding to the DFS traversal and descendants_at_distance does the same as its BFS counterpart, just using DFS as a base to traverse. As such, we will focus on the remaining: dfs_edges, dfs_labelled_edges, and the preorder and postorder node functions. From 2defd7c028a2433c137b563a06438e609553d746 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:46:51 -0400 Subject: [PATCH 29/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index 7ab43eda..bff84614 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -565,7 +565,7 @@ We have six use cases. dfs_predecessors and dfs_successors do essentially the same as their BFS counterparts but with DFS, so we'll ignore those for now. Likewise, dfs_tree simply builds a tree corresponding to the DFS traversal and descendants_at_distance does the same as its BFS counterpart, just using DFS as a base to traverse. -As such, we will focus on the remaining: dfs_edges, dfs_labelled_edges, and the preorder and postorder node functions. +As such, we will focus on the remaining: dfs_edges, dfs_labeled_edges, and the preorder and postorder node functions. We can dfs_edges to explicitly show all the edges that are traversed in the above graph during a DFS. Check it out below. It matches our animation as well! From 2062446f4442f7b12f6472d08a8b68dcd962e14a Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:46:58 -0400 Subject: [PATCH 30/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index bff84614..b62920b0 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -567,7 +567,7 @@ dfs_predecessors and dfs_successors do essentially the same as their BFS counter As such, we will focus on the remaining: dfs_edges, dfs_labeled_edges, and the preorder and postorder node functions. -We can dfs_edges to explicitly show all the edges that are traversed in the above graph during a DFS. Check it out below. It matches our animation as well! +We can use dfs_edges to explicitly show all the edges that are traversed in the above graph during a DFS. Check it out below. It matches our animation as well! ```{code-cell} ipython3 dfs_edges_result = list(nx.dfs_edges(G, 0)) From 35360d26e603c815ff9b60939018b7bd6a20bb96 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:47:08 -0400 Subject: [PATCH 31/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index b62920b0..2c31c02e 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -570,9 +570,8 @@ As such, we will focus on the remaining: dfs_edges, dfs_labeled_edges, and the p We can use dfs_edges to explicitly show all the edges that are traversed in the above graph during a DFS. Check it out below. It matches our animation as well! ```{code-cell} ipython3 -dfs_edges_result = list(nx.dfs_edges(G, 0)) print("Edges traversed during BFS traversal:") -for edge in dfs_edges_result: +for edge in nx.dfs_edges(G, 0): print(edge) ``` From d2b156868bab0e919262c24f2f6122dbc4a56697 Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:47:20 -0400 Subject: [PATCH 32/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index 2c31c02e..be7115fd 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -683,7 +683,7 @@ plt.tight_layout() plt.show() ``` -DFS postorder is an ordering in which we visit a node's children first before processing the node itself. Starting from the source node, we traverse deeper into the graph, visiting all the child nodes before processing the current node. Once all the child nodes are visited, we backtrack and process the current node. This order is often used to compute the size of subtrees and perform tasks that depend on information from the children. +DFS postorder is a node ordering in which we report a node's children first before reporting the node itself. Starting from the source node, we traverse deeper into the graph, visiting all the child nodes before reporting the current node. Once all the child nodes are visited, we backtrack and report the current node. This order is often used to compute the size of subtrees and perform tasks that depend on information from the children. On the other hand, DFS preorder is an ordering in which we process the current node before visiting its children. We start from the source node, process it, then move to its first child, process it, and so on, until all children are processed. Only then do we backtrack and continue processing other children of the parent node. This order is useful for tasks that require processing nodes before their children, such as copying the entire tree structure. From 70130c8a40a3ac71943c7ae037bab3e5807a6fec Mon Sep 17 00:00:00 2001 From: 20kavishs <123114628+20kavishs@users.noreply.github.com> Date: Sat, 26 Aug 2023 11:47:29 -0400 Subject: [PATCH 33/36] Update content/algorithms/traversal/index.md Co-authored-by: Dan Schult --- content/algorithms/traversal/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/algorithms/traversal/index.md b/content/algorithms/traversal/index.md index be7115fd..ff6e7364 100644 --- a/content/algorithms/traversal/index.md +++ b/content/algorithms/traversal/index.md @@ -685,7 +685,7 @@ plt.show() DFS postorder is a node ordering in which we report a node's children first before reporting the node itself. Starting from the source node, we traverse deeper into the graph, visiting all the child nodes before reporting the current node. Once all the child nodes are visited, we backtrack and report the current node. This order is often used to compute the size of subtrees and perform tasks that depend on information from the children. -On the other hand, DFS preorder is an ordering in which we process the current node before visiting its children. We start from the source node, process it, then move to its first child, process it, and so on, until all children are processed. Only then do we backtrack and continue processing other children of the parent node. This order is useful for tasks that require processing nodes before their children, such as copying the entire tree structure. +On the other hand, DFS preorder is a node ordering in which we process the current node before visiting its children. We start from the source node, process it, then move to its first child, process it, and so on, until all first children are processed. Only then do we backtrack one edge and continue processing other children of that parent node. This order is useful for tasks that require processing nodes before their children, such as copying the entire tree structure. +++ From 1dbb9edbf14853930769d39bb0ead2c7ef0f9723 Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Sun, 27 Aug 2023 15:09:51 -0400 Subject: [PATCH 34/36] try getting notebooks tested on this branch --- .github/workflows/notebooks.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml index dd9813cf..6edc0875 100644 --- a/.github/workflows/notebooks.yml +++ b/.github/workflows/notebooks.yml @@ -45,10 +45,11 @@ jobs: pip install pre-commit find content/ -name "*.md" -exec jupytext --to notebook {} \; # pre-commit wants files to be staged - git add content/ + find content/ -name "*.ipynb" -exec git commit add {} \; pre-commit run --all-files --show-diff-on-failure --color always - name: Test with nbval run: | + find content/algorithms content/generators -name "*.ipynb" -print pip install pytest - pytest --nbval-lax --durations=25 content/ + pytest --nbval-lax --durations=25 content/algorithms content/generators From d75efd05b6237d54578e230703448bc39ce45af2 Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Sun, 27 Aug 2023 17:53:33 -0400 Subject: [PATCH 35/36] fix typo in linting --- .github/workflows/notebooks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml index cbd9494a..7e3df27a 100644 --- a/.github/workflows/notebooks.yml +++ b/.github/workflows/notebooks.yml @@ -45,7 +45,7 @@ jobs: pip install pre-commit find content/ -name "*.md" -exec jupytext --to notebook {} \; # pre-commit wants files to be staged - find content/ -name "*.ipynb" -exec git commit add {} \; + find content/ -name "*.ipynb" -exec git add {} \; pre-commit run --all-files --show-diff-on-failure --color always - name: Test with nbval From 9a98824820064afeca99589c7a657f6c8b9860ab Mon Sep 17 00:00:00 2001 From: Dan Schult Date: Mon, 28 Aug 2023 07:51:37 -0400 Subject: [PATCH 36/36] fix merge induced typo --- content/algorithms/euler/euler.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index a6fd5287..0a86f844 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -14,6 +14,8 @@ kernelspec: # Euler's Algorithm ++++ + In this tutorial, we will explore Euler's algorithm and its implementation in NetworkX under [`networkx/algorithms/euler.py`](https://github.com/networkx/networkx/blob/main/networkx/algorithms/euler.py). ## Import package