# NetworkX Tutorial¶

In this tutorial we will take a look at ways of combining the analysis tools provided by NetworkX with the visualization capailities of AlgorithmX.

## Simple Graph¶

Let’s start by creating a simple NetworkX graph. We will use add_path to quickly add both nodes and edges.

```import networkx as nx

G = nx.Graph()

print('Nodes:', G.nodes)
print('Edges:', G.edges)
```
```Nodes: [1, 2, 3, 4, 5]
Edges: [(1, 2), (2, 3), (2, 4), (2, 5)]
```

Now that we have all the data we need, we can create an AlgorithmX canvas to display our nodes and edges.

```import algorithmx

canvas = algorithmx.jupyter_canvas()

canvas
```

So we have our simple graph, but we think it could look a little more interesting. Let’s define a custom style for our nodes, and also give each one a different color. We can take advantage of the fact that nearly any argument in AlgorithmX can be passed as a lambda function, making our code much more concise.

```canvas = algorithmx.jupyter_canvas()

node_style = {
'shape': 'rect',
'size': (20, 12)
}
node_colors = {1: 'red', 2: 'green', 3: 'blue', 4: 'orange', 5: 'purple'}

.set(node_style) \
.color(lambda n: node_colors[n])

canvas
```

Making the graph directed is easy - all we have to do is call G.to_directed(), and then tell AlgorithmX that the edges should be rendered with an arrow.

Weighted and Directed Graphs To create a directed graph, all we need to do is use a NetworkX DiGraph, and tell AlgrithmX that edges should be rendered with an arrow.

```G = nx.DiGraph()

G.add_edges_from([(1, 2), (2, 3), (3, 1)])

canvas = algorithmx.jupyter_canvas()

canvas
```

To create wighted graph, we will first ensure that our NetworkX edges have a ‘weight’ attribute. Then, we will add a label to each edge displaying the attribute.

```G = nx.Graph()

G.add_weighted_edges_from([(1, 2, 0.4), (2, 3, 0.2), (3, 1, 0.3)])

canvas = algorithmx.jupyter_canvas()

.text(lambda e: G.edges[e]['weight'])

canvas
```

Finally, AlgorithmX provides a uility to simplify this process.

```from algorithmx.networkx import add_graph

G = nx.DiGraph()

G.add_weighted_edges_from([(1, 2, 0.4), (2, 3, 0.2), (3, 1, 0.3)])

canvas = algorithmx.jupyter_canvas()

```

## Random Graph¶

NetworkX provides a range of functions for generating graphs. For generating a random graph, we will use the basic gnp_random_graph function. By providing a seed, we can ensure that we get the same graph every time (otherwise there is no guarantee of it being connected!).

```G = nx.gnp_random_graph(10, 0.3, 138)

canvas = algorithmx.jupyter_canvas()

canvas
```

To make the graph directed, we will simply use G.to_directed. To make the graph weighted, we will need to configure a weight attribute for each edge. Since our graph is random, we’ll make our edge weights random as well. For this we will use the set_edge_attributes function.

```from random import randint

G = G.to_directed()
nx.set_edge_attributes(G, {e: {'weight': randint(1, 10)} for e in G.edges})
```

We can now display the graph using the utility from before.

```canvas = algorithmx.jupyter_canvas()
```

## Detailed Graph¶

Now we are going to create a graph that displays a range of interesting properties. Let’s begin by generating a random weighted graph, as before.

```G = nx.gnp_random_graph(10, 0.3, 201)
nx.set_edge_attributes(G, {e: {'weight': randint(1, 10)} for e in G.edges})
```

Next, we will use NetworkX to calculate the graph’s coloring and edge centrality.

```coloring = nx.greedy_color(G)
centrality = nx.edge_betweenness_centrality(G, weight='weight', normalized=True)
```

We can now begin displaying the graph. First, we will add the nodes and assign them a color based on their calculated priority. We happen to know that any graph requires at most 4 different colors, and so we prepare these beforehand.

```canvas = algorithmx.jupyter_canvas()

color_priority = {0: 'red', 1: 'orange', 2: 'yellow', 3: 'green'}

.color(lambda n: color_priority[coloring[n]])

print(coloring)
```
```{4: 0, 2: 1, 3: 2, 0: 1, 1: 2, 6: 0, 8: 1, 7: 2, 9: 2, 5: 0}
```
```
```

Afterwards, we will add the edges. Each one will have two labels; one to display it’s weight, and another to display it’s calculated centrality.

```init_edges = canvas.edges(G.edges).add()

formatted_centrality = {k: '{0:.2f}'.format(v) for k, v in centrality.items()}

.text(lambda e: G.edges[e]['weight']) \

```{(0, 1): '0.00', (0, 3): '0.11', (0, 4): '0.09', (1, 4): '0.00', (1, 8): '0.20', (2, 3): '0.47', (2, 4): '0.00', (2, 5): '0.20', (2, 6): '0.00', (2, 7): '0.20', (3, 4): '0.44', (3, 6): '0.20', (4, 8): '0.47', (4, 9): '0.00', (6, 7): '0.00', (8, 9): '0.20'}
```canvas