Distance between nodes LightGraphs julia - graph

I am wondering about how to compute the distance between a given pair of nodes, say nodes "i" and "j"
This is a minimal example for say nodes 2 and 12 from a Random Regular Graph with 100 nodes and connectivity 3
julia> using LightGraphs
julia> L = random_regular_graph(100, 3)
julia> paths= dijkstra_shortest_paths(L, 2)
julia> distances = paths.dists
julia> d = distances[12]
The problem with this approach is that I have to calculate the distances between all the nodes and my node 2 in order to know the distance between my two nodes of interest

If you just need the shortest path from a specific source to a specific destination, consider using A*:
julia> g = CycleGraph(10)
{10, 10} undirected simple Int64 graph
julia> a_star(g, 1, 8)
3-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 10
Edge 10 => 9
Edge 9 => 8
If you are JUST interested in the (unweighted, unit) distances, use gdistances:
julia> gdistances(g, 1)[8]
3
In any case, do not access the .dists field from the DijkstraResult. Use the dists() method as the accessor. The internals of LightGraphs structs are not intended to be used directly.

Related

How to avoid unhashable type 'list' when checking to see if maximal cliques are contained in another graph in NetworkX Library?

I would like to be able to compare the maximal cliques of two graphs G and H, in the networkX library, and store the maximal cliques of G which are not maximal cliques in H as a set (i.e., having no duplicates).
This is the code I am working with, but when running this I get an error
max_cliques_H = set(nx.find_cliques(H)) ^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list'
Any help is appreciated!
import networkx as nx
# Create graph G and H
G = nx.complete_graph(5)
H = nx.complete_graph(7)
# Find all maximal cliques in G
max_cliques_G = list(nx.find_cliques(G))
# Find all maximal cliques in H
max_cliques_H = set(nx.find_cliques(H))
# Initialize an empty set to store cliques that are not in H
not_in_H = set()
# Iterate over all maximal cliques in G
for clique_G in max_cliques_G:
# Check if the clique from G is not a maximal clique in H
if clique_G not in max_cliques_H:
not_in_H.add(clique_G)
print(f"Cliques that are not in H : {not_in_H}")
I've tried to write a program that will perform the specified operation, and I was expecting to get a set (i.e., with no duplicates) of maximal cliques in G which are not maximal cliques in H, but instead I got an error.
EDIT I realized my example is not good, since the complete graph on 5 vertices is a subgraph of the complete graph on 7 vertices. I think if you swap G and H then my question makes more sense, though in general I want to compare graphs with the same vertex set but different edges. At amy rate, I still get the unhashable list error.

Why do we have at least three definitions of a graph in math?

Definition 1 - 2 sets and function
Definitioin 2 - 1 set and 1 family
Definition 3 - 1 relation
Why do we need such a diversity? Are some of these definitions old-fashioned or all of them have their pros and cons?
Undirected graphs and directed graphs
The third definition differs from the first two because it is about directed graphs, while the first two define undirected graph. We care about directed graphs and undirected graphs because they are adapted to different situations and to solving different problems.
You can think of directed graphs and undirected graphs as two different objects.
In general, undirected graphs are somewhat easier to reason about, and most often if someone mentions a "graph" without precision, they mean an undirected graph.
Named edges and incidence function
The first two definitions are pretty much equivalent.
The first definition, with (V, E, ѱ) gives "names" to vertices (elements of V) and names to edges (elements of E), and uses an "incidence function" ѱ to tell you which edge in E corresponds to which pair of vertices of V.
The second definition uses only (V', E') and no ѱ. I am calling them V' and E' instead of V and E to make a distinction from the first definition. Here the vertices have "names", they are the elements of V'; but the edges don't really have individual names, and E' is defined as a subset of the set of undirected pairs of V. Therefore an edge is an undirected pair of elements of V'.
Here is an example of a graph:
By the first definition:
V = {a, b, c, d};
E = {1, 2, 3};
ѱ : E -> {unordered pairs of V}
1 -> ab
2 -> ac
3 -> cd.
By the second definition:
V' = {a, b, c, d}
E' = {ab, ac, cd}.
As you can see, V' = V, and E' is the image of E by ѱ.
If you don't care about "names" for the edges, the second definition is somewhat shorter. But which one you use really doesn't matter; the theorems you might prove with one definition will be equivalent to the theorems you can prove for the other definition. The difference between the two definition is just a set theory nitpick of what "edge" means: is it a pair of elements of V, or an element of another set which is mapped to a pair of elements of V by a function? Note that the function ѱ is a bijection between the two sets E and E', so really E and E' are two different names for the same set.
Algorithms, programming languages, and representations of graphs
If you ever have to code an algorithm using a graph in your favourite programming language, you will have to decide how to represent the graph using variables and arrays and all the data structures you are used to.
For the vertices, most often, people use V = {0, 1, 2, ..., n-1} where n is the number of vertices. This is convenient because it means you can use the vertices as indices for an array.
For the edges, sometimes we encode E using an vertex-vertex incidence matrix of size n*n with a 1 in cell i,j to indicate an edge between vertices i and j and a 0 in cell i,j to indicate no edge. Here is the incidence matrix for the graph above (I replaced a,b,c,d with 0,1,2,3 as the names for the vertices):
0 1 2 3
0 0 1 1 0
1 1 0 0 0
2 1 0 0 1
3 0 0 1 0
Sometimes we encode E using an array of lists: an array of size n, where cell i contains the list of indices of vertices which are neighbours of vertex i. Here is the array of lists for the same graph:
0: 1,2
1: 0
2: 0,3
3: 2
Those two representations are closer to the second definition, since the edges don't have names; we just care about whether each pair of vertices is an edge or not.
Recently I had to write a C++ program where it was very important for me to number the edges, because I wanted to be able to use them as indices of a matrix. Thus I had V = {0, 1, 2, ..., n-1}; E = {0, 1, 2, ..., m-1}; and then I used an std::map<int, std::pair<int, int>> to map the edge indices to pairs of vertex indices. This representation was closer to your first definition, which an std::map for ѱ. Note that I had to make a choice between mapping edge indices to pairs of vertex indices, or mapping pairs of vertex indices to edge indices. Had I felt the necessity, I could even have used both. The first definition doesn't care, because ѱ is a bijection, so mathematicians can use ѱ and its inverse function ѱ^-1 indifferently; but the data structure std::map is not a mathematical function, and inversing it might take time.
Conclusion
Both definitions are equivalent, and it really doesn't matter which one you use. But if you need to code algorithms using graphs, take some time to consider different representations of graphs and which one will make your algorithm the most efficient.

Checking validity of topological sort

Given the following directed graph:
I determined the topological sort to be 0, 1, 2, 3, 7, 6, 5, 4 with the values for each node being:
d[0] = 1
f[0] = 16
d[1] = 2
f[1] = 15
d[2] = 3
f[2] = 14
d[3] = 4
f[3] = 13
d[4] = 7
f[4] = 8
d[5] = 6
f[5] = 9
d[6] = 5
f[6] = 10
d[7] = 11
f[7] = 12
Where d is discovery-time and f is finishing-time.
How can I check whether the topological sort is valid or not?
With python and networkx, you can check it as follows:
import networkx as nx
G = nx.DiGraph()
G.add_edges_from([(0, 2), (1, 2), (2, 3)])
all_topological_sorts = list(nx.algorithms.dag.all_topological_sorts(G))
print([0, 1, 2, 3] in all_topological_sorts) # True
print([2, 3, 1, 0] in all_topological_sorts) # False
However, note that in order to have a topological ordering, the graph must be a Directed Acyclic Graph (DAG). If G is not directed, NetworkXNotImplemented will be raised. If G is not acyclic (as in your case) NetworkXUnfeasible will be raised.
See documentation here.
If you want a less coding approach to this question (since it looks like your original topological ordering was generated without code), you can go back to the definition of a topological sort. Paraphrased from Emory University:
Topological ordering of nodes = an ordering (label) of the nodes/vertices such that for every edge (u,v) in G, u appears earlier than v in the ordering.
There's two ways that you could approach this question: from an edge perspective of a vertex perspective. I describe a naive (meaning with some additional space complexity and cleverness, you could improve on them) implementation of both below.
Edge approach
Iterate through the edges in G. For each edge, retrieve the index of each of its vertices in the ordering. Compared the indices. If the origin vertex isn't earlier than the destination vertex, return false. If you iterate through all of the edges without returning false, return true.
Complexity: O(E*V)
Vertex approach
Iterate through the vertices in your ordering. For each vertex, retrieve its list of outgoing edges. If any of those edges end in a vertex that precedes the current vertex in the ordering, return false. If you iterate through all the vertices without returning false, return true.
Complexity: O(V^2*E)
First, do a graph traversal to get the incoming degree of each vertex. Then start from the first vertex in your list. Every time, when we look at a vertex, we want to check two things 1) is the incoming degree of this vertex is 0? 2) is this vertex a neighbor of the previous vertex? We also want to decrement all its neighbors' incoming degree, as if we cut all edges. If we got a no from the previous questions at some point, we know that this is not a valid topological order. Otherwise, it is. This takes O(V + E) time.

Generating a weighted and directed network form adjacency matrix in Julia

I want to generate a weighted and directed network from an adjacency matrix in Julia (v0.7).
So far I've tried:
using LightGraphs
using SimpleWeightedGraphs
A = rand(100, 100)
G = Graph(A)
but I get error:
ERROR: ArgumentError: Adjacency / distance matrices must be symmetric
Stacktrace:
[1] SimpleGraph{Int64}(::Array{Float64,2}) at /home/user/.julia/packages/LightGraphs/PPsyP/src/SimpleGraphs/simplegraph.jl:78
[2] SimpleGraph(::Array{Float64,2}) at /home/user/.julia/packages/LightGraphs/PPsyP/src/SimpleGraphs/simplegraph.jl:72
[3] top-level scope at none:0
So far I have only seen the example on the github (https://github.com/JuliaGraphs/SimpleWeightedGraphs.jl) page which generates the weighted graph from and edgelist. However, I would prefer if I could generate the graph directly from an adjacency matrix.
Building off crstnbr's answer, a Graph is an unweighted undirected, so the adjacency matrix is ideally symmetric with values in [0, 1].
Feeding the Graph constructor any symmetric matrix creates edges for every non-zero element:
A = rand(3,3);
Graph(A+A');
println.(edges(G));
Edge 1 => 1
Edge 1 => 2
Edge 1 => 3
Edge 2 => 2
Edge 2 => 3
Edge 3 => 3
The SimpleWeightedDiGraph has several constructors that can take a dense or SparseMatrixCSC adjacency matrix:
SimpleWeightedDiGraph(rand(4,4))
{4, 16} directed simple Int64 graph with Float64 weights
SimpleWeightedDiGraph(rand([0,1], 3, 3))
{3, 5} directed simple Int64 graph with Int64 weights
using SparseArrays
SimpleWeightedDiGraph( sprand(3, 3, 0.5) )
{3, 5} directed simple Int64 graph with Float64 weights
By no means a Julia graph expert, but I think what you want is
julia> A = rand(100,100);
julia> G = SimpleWeightedDiGraph(A)
{100, 10000} directed simple Int64 graph with Float64 weights
Graph(a::AbstractMatrix) is the constructor for an undirected (unit-weighted) graph:
julia> A = A+transpose(A); # making A symmetric
julia> G = Graph(A)
{100, 5050} undirected simple Int64 graph
julia> weights(G)
100 × 100 default distance matrix (value = 1)
The first problem you're running into is that your random adjacency matrix is not symmetric, and that's required for an undirected graph. You want to create a directed graph.
Secondly, if you want a weighted graph, you'll want to use the SimpleWeightedGraphs.jl package, which means that you can simply do
julia> using LightGraphs, SimpleWeightedGraphs
julia> a = rand(100,100);
julia> g = SimpleWeightedDiGraph(a)
{100, 10000} directed simple Int64 graph with Float64 weights
but note that this is a really bad way to create a random weighted graph because the rand function all but guarantees that this will be a complete graph. Much better is to use sprand:
julia> using SparseArrays
julia> a = sprand(100, 100, 0.2);
julia> g = SimpleWeightedDiGraph(a)
{100, 2048} directed simple Int64 graph with Float64 weights

Graph Theory (degree of the vertices of a graph when it can be partitioned into two trees)

Show that if the edge set of a graph G(V,E) with n nodes
can be partitioned into 2 trees,
then there is at least one vertex of degree less than 4 in G.
...................................................................................
I have tried to prove this problem with the help of the method of contradiction.
Assume that all vertices of the graph G has degree >= 4.
Assume the graph G is partitioned into two trees T1 and T2.
With the help of the above assumptions the only observation I could make is that for every vertex v in G
degree of v must be greater than or equal to 2 in either T1 or T2.
I don't know how proceed with this. Please help.
If my approach for solving this problem is wrong then please provide a different solution.
You started with a good approach. Lets assume all vertices in G has degree of 4 (or above) and sssume the graph G is partitioned into two trees T1 and T2.
We know that number of edge in tree is n-1 (when n is number of vertices). Therefor in each of T1 and T2 we have n-1 edges (consider n to be |V|) -> combine we have 2n-2 edges in G -> |E| = 2n-2
From the other hand we know that each v in G -> d(v) > 4 . And we know that sum of degree in graph equal to 2|E|. therefor, 2*|E| >= 4*n (I took the minimum degree for each vertex and each edge contribute 2 to the sum of the degree). So we got |E| >= 2*n.
Contradiction -> There is have to be one vertex with degree less then 4

Resources