diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml index 172f00bc..3e27bd4f 100644 --- a/.github/workflows/notebooks.yml +++ b/.github/workflows/notebooks.yml @@ -47,5 +47,7 @@ jobs: - name: Test with nbval run: | + find content/algorithms content/generators -name "*.md" -exec jupytext --to notebook {} \; + 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 diff --git a/content/algorithms/assortativity/correlation.md b/content/algorithms/assortativity/correlation.md index 1ec8cf4c..7ed52471 100644 --- a/content/algorithms/assortativity/correlation.md +++ b/content/algorithms/assortativity/correlation.md @@ -22,16 +22,29 @@ language_info: version: 3.8.5 --- -# 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 [^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. +as well as mixing matrices, which are closely related 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 @@ -80,7 +93,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 +106,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 +118,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,25 +143,14 @@ It is implemented as `degree_assortativity_coefficient` and `scipy.stats.pearsonr` to calculate the assortativity coefficient which makes it potentally faster. -## Example +## Assortativity Example -```{code-cell} ipython3 -%matplotlib inline -import networkx as nx -import matplotlib.pyplot as plt -import pickle -import copy -import random -import warnings - -warnings.filterwarnings("ignore") -``` ++++ 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) @@ -261,6 +263,10 @@ are drawn. +++ -[^1]: M. E. J. Newman, Mixing patterns in networks +## References + +[^1]: [Wikipedia, Assortativity](https://en.wikipedia.org/wiki/Assortativity) + +[^2]: M. E. J. Newman, Mixing patterns in networks -[^2]: 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 diff --git a/content/algorithms/dag/index.md b/content/algorithms/dag/index.md index 9f362bd2..83f25e60 100644 --- a/content/algorithms/dag/index.md +++ b/content/algorithms/dag/index.md @@ -25,25 +25,24 @@ 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`. +(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 -```{code-cell} ipython3 -triangle_graph = nx.from_edgelist([(1, 2), (2, 3), (3, 1)], create_using=nx.DiGraph) +%matplotlib inline ``` +## Example: Directed Graphs + ```{code-cell} ipython3 +triangle_graph = nx.DiGraph([(1, 2), (2, 3), (3, 1)]) nx.draw_planar( triangle_graph, with_labels=True, @@ -75,7 +74,6 @@ clothing_graph = nx.read_graphml(f"data/clothing_graph.graphml") ```{code-cell} ipython3 plt.figure(figsize=(12, 12), dpi=150) - nx.draw_planar( clothing_graph, arrowsize=12, @@ -164,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) +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. @@ -343,8 +341,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 +349,7 @@ 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) diff --git a/content/algorithms/euler/euler.md b/content/algorithms/euler/euler.md index 640aa849..0a86f844 100644 --- a/content/algorithms/euler/euler.md +++ b/content/algorithms/euler/euler.md @@ -14,7 +14,15 @@ kernelspec: # Euler's Algorithm -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 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 + +```{code-cell} +import networkx as nx +``` ## Seven Bridges of Königsberg @@ -37,8 +45,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} -import networkx as nx - +# Create graph G = nx.DiGraph() G.add_edge("A", "B", label="a") G.add_edge("B", "A", label="b") @@ -50,6 +57,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="-" @@ -73,9 +81,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} +# Design and draw graph edge_labels = nx.get_edge_attributes(G, "label") nx.draw_networkx_nodes(G, pos=positions, node_size=500) @@ -117,7 +126,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 +## 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 @@ -282,5 +291,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 23d77112..c75b2dfa 100644 --- a/content/algorithms/flow/dinitz_alg.md +++ b/content/algorithms/flow/dinitz_alg.md @@ -22,13 +22,15 @@ language_info: version: 3.10.1 --- -# 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 [^1] and Dinitz's +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,7 +42,10 @@ 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 interesting thing about this peer-to-peer network is that it allows you to send data @@ -48,8 +53,8 @@ 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) pos = {k: np.asarray(v) for k, v in G.nodes(data="pos")} label_pos = deepcopy(pos) @@ -95,8 +100,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 +115,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) @@ -539,7 +548,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) @@ -634,6 +643,8 @@ 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/algorithms/lca/LCA.md b/content/algorithms/lca/LCA.md index ba8bf73c..cb981c3e 100644 --- a/content/algorithms/lca/LCA.md +++ b/content/algorithms/lca/LCA.md @@ -13,7 +13,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 @@ -26,6 +37,7 @@ 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$. + ## Example It is always a good idea to learn concepts with an example. Consider the following evolutionary tree. We will draw a directed version of it and define the ancestor/descendant relationships. @@ -34,13 +46,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( @@ -190,3 +195,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 8529dc00..ce461f4e 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. @@ -31,18 +31,20 @@ 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 facebook = pd.read_csv( @@ -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 component: ```{code-cell} ipython3 nx.number_connected_components(G) @@ -546,7 +548,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 +576,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 +598,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) [^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 fb300846..bcca2b40 100644 --- a/content/generators/geometric.md +++ b/content/generators/geometric.md @@ -25,9 +25,18 @@ language_info: # 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 +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 @@ -173,11 +182,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,6 +348,8 @@ for thresh, ax in zip(thresholds, axes): fig.tight_layout() ``` +## References + [^1]: Spatial Networks [^2]: Random Geometric Graphs diff --git a/content/generators/sudoku.md b/content/generators/sudoku.md index 13ee21e3..4acc6630 100644 --- a/content/generators/sudoku.md +++ b/content/generators/sudoku.md @@ -22,9 +22,20 @@ language_info: version: 3.9.7 --- -# Sudoku and Graph coloring +# 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. @@ -66,8 +77,7 @@ 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], @@ -84,10 +94,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())) @@ -98,6 +104,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, @@ -254,3 +261,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) diff --git a/requirements.txt b/requirements.txt index 72ea401c..6fa8a033 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,9 @@ sphinx myst-nb sphinx-book-theme jupytext +black +pyupgrade # Notebook requirements pygraphviz +