Skip to content

Commit

Permalink
Improve consistency across notebooks (#98)
Browse files Browse the repository at this point in the history
* Standardize

* Update sudoku.md

* Changed links back to old form

* change versions back to original

* add back ipykernel

* clean up, make pre-commit happy

* make pre-commit happy

* Fix typo.

* Rm unnecessary warnings filter.

* Fix footnote format.

* Condense heading.

* More canonical way to create DiGraph.

* Condense headings.

* Fix header level in flow.

* Typo.

* Fix heading level.

* More heading fixes.

* Resolved suggestions

* MAINT: changing white text logo to gray banner (#105)

* Isomorphism tutorial (#83)

Add a high-level notebook introducing isomorphism and the related functionality
in NetworkX

Co-authored-by: Mridul Seth <[email protected]>
Co-authored-by: Ross Barnowski <[email protected]>
Co-authored-by: Mridul Seth <[email protected]>

* Add Python 3.11 to the CI. (#107)

* Rm colab cruft. (#106)

* created and then removed traversal notebook draft 1

* try converting with jupytext for tests again

* test all notebooks except the long exploratory ones

* restore the python versions field in tests

* Fixup missing ref in Dinitz nb.

---------

Co-authored-by: Ross Barnowski <[email protected]>
Co-authored-by: Mridul Seth <[email protected]>
Co-authored-by: Dan Schult <[email protected]>
  • Loading branch information
4 people authored Aug 27, 2023
1 parent fd60ff4 commit 991e83d
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 86 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/notebooks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 28 additions & 22 deletions content/algorithms/assortativity/correlation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]$

Expand All @@ -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} $$

Expand All @@ -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).
Expand All @@ -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)
Expand Down Expand Up @@ -261,6 +263,10 @@ are drawn.

+++

[^1]: M. E. J. Newman, Mixing patterns in networks <https://doi.org/10.1103/PhysRevE.67.026126>
## References

[^1]: [Wikipedia, Assortativity](https://en.wikipedia.org/wiki/Assortativity)

[^2]: M. E. J. Newman, Mixing patterns in networks <https://doi.org/10.1103/PhysRevE.67.026126>

[^2]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks <https://doi.org/10.1073/pnas.0912671107>
[^3]: Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M. Edge direction and the structure of networks <https://doi.org/10.1073/pnas.0912671107>
24 changes: 12 additions & 12 deletions content/algorithms/dag/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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))
```

Expand All @@ -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)
24 changes: 17 additions & 7 deletions content/algorithms/euler/euler.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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")
Expand All @@ -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="-"
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -282,5 +291,6 @@ Euler's algorithm is essential for anyone or anything that uses paths. Some exam

## References

[^1]: <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.
[^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.
27 changes: 19 additions & 8 deletions content/algorithms/flow/dinitz_alg.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -40,16 +42,19 @@ 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
along the paths you specify with certain limits on the sizes of data per second that
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)
Expand Down Expand Up @@ -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]);
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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. <https://doi.org/10.1007/11685654_10>
26 changes: 18 additions & 8 deletions content/algorithms/lca/LCA.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand All @@ -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(
Expand Down Expand Up @@ -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)
Loading

0 comments on commit 991e83d

Please sign in to comment.