Skip to content

Commit

Permalink
Merge branch 'networkx:main' into feature/graph-colouring
Browse files Browse the repository at this point in the history
  • Loading branch information
unna97 authored Apr 16, 2023
2 parents 97505d8 + 951656f commit fd4714f
Show file tree
Hide file tree
Showing 4 changed files with 0 additions and 58 deletions.
6 changes: 0 additions & 6 deletions content/algorithms/euler/euler.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ kernelspec:

# Euler's Algorithm

+++

In this tutorial, we will explore the Euler's algorithm and its implementation in NetworkX under `networkx/algorithms/euler.py`.

## 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.

+++

![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]:
Expand Down Expand Up @@ -208,8 +204,6 @@ If an undirected graph is not Eulerian, it can still be `semi_eulerian` meaning
- there are exactly two vertices of odd degree, and
- all of its vertices belong to a single connected component.

+++

If source vertex is given by the user, it must have an odd degree. Otherwise, there cannot be an Eulerian Path starting from the given source.

```python
Expand Down
9 changes: 0 additions & 9 deletions content/algorithms/index.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@



+++

+++

+++

# Algorithms

A closer look at some of the algorithms and network analysis techniques
Expand Down
34 changes: 0 additions & 34 deletions content/algorithms/lca/LCA.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ kernelspec:
name: python3
---

+++ {"id": "jrizb5yufXBG"}

# 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).
Expand All @@ -28,16 +26,12 @@ 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

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.

![image:evolutionary tree](images/evol_tree.png)

+++ {"id": "-mvVopP42kk9"}

Let's first draw the tree using NetworkX.

```{code-cell}
Expand Down Expand Up @@ -77,8 +71,6 @@ nx.draw(
plt.show()
```

+++ {"id": "MziCQi2akACo"}

Consider the tree above and observe the following relationships:

- Ancestors of node Mammal:
Expand All @@ -93,18 +85,12 @@ Consider the tree above and observe the following relationships:

_Note that, in terms of lowest common ancestor algorithms, every node is considered as an ancestor itself._

+++ {"id": "mjEM8pgNolIo"}

## NetworkX's Implementation of Lowest Common Ancestor Algorithm

NetworkX uses a naive algorithm to find the lowest common ancestor of given pairs of nodes. In this section, we will introduce it step by step.

+++ {"id": "Lx2DUlo7DUdN"}

### Step 1: Check if the type of input graph is DAG.

+++ {"id": "sIMW9IoLtNeU"}

Lowest common ancestor algorithms under NetworkX are implemented only for directed acyclic graphs with at least one node. For this, the source code first checks if the input graph is a valid one or not.

```python
Expand All @@ -115,8 +101,6 @@ def naive_all_pairs_lowest_common_ancestor(G, pairs=None):
raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
```

+++ {"id": "C9wWNKYzzCPb"}

If the "pairs" argument is not set, we consider all unordered pairs of nodes in G by default, e.g. we do not get both (b, a) and (a, b) but only one of them. If pairs are already specified, we check if every node in pairs exists in the input graph.

```python
Expand All @@ -133,12 +117,8 @@ else:
)
```

+++ {"id": "GGG-GpILHHcj"}

### Step 2: Find ancestors of all nodes in G.

+++ {"id": "j2lFxyq-6ixI"}

Once the input validation is done, we find all ancestors of every node in the pairs and store these information in a cache.

```python
Expand All @@ -153,24 +133,16 @@ for v, w in pairs:
ancestor_cache[w].add(w)
```

+++ {"id": "hHSvAFp1jW_a"}

### Step 3: Find common ancestors

+++ {"id": "ZHWKa9WT60bG"}

For each pair (v, w), we determine nodes that appear in both ancestor lists of $v$ and $w$. (i.e. find all common ancestors)

```python
common_ancestors = ancestor_cache[v] & ancestor_cache[w]
```

+++ {"id": "enpNSvkofqqJ"}

### Step 4: Find a node in common ancestors which is located at the lowest level in the graph.

+++ {"id": "ZY_BBL0c05tp"}

We start with an arbitrary node $v$ from the set of common ancestors. We follow the arbitrary outgoing edges remaining in the set of common ancestors, until reaching a node with no outgoing edge to another of the common ancestors.

```python
Expand All @@ -186,8 +158,6 @@ while True:
v = successor
```

+++ {"id": "8C-SlZeR7ovl"}

We can see the result of our algorithm for a simple directed acyclic graph. Assume that our graph G is as follows and we wish to find lowest common ancestors for all pairs. For this, we need to call the `all_pairs_lowest_common_ancestor`
method.

Expand Down Expand Up @@ -215,12 +185,8 @@ plt.show()
dict(nx.all_pairs_lowest_common_ancestor(G))
```

+++ {"id": "K3QvlQd0-sSB"}

## Time & Space Complexity

+++ {"id": "gW37WqW2-yQk"}

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).
9 changes: 0 additions & 9 deletions content/generators/index.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@



+++

+++

+++

# Graph Generators

A closer look at the functions provided by NetworkX to create interesting
Expand Down

0 comments on commit fd4714f

Please sign in to comment.