igraph / python-igraph

Python interface for igraph
GNU General Public License v2.0
1.31k stars 249 forks source link

is_directed() method not working properly? #777

Closed mcharrak closed 6 months ago

mcharrak commented 6 months ago

Describe the bug The is_directed() method returns True for graphs that are undirected.

To reproduce


# include the necessary imports to run the below code
import networkx as nx
import numpy as np
import igraph as ig

# do a toy example: 1. create symmetric matrix 2. convert to igraph 3. check if it is directed
# create symmetric matrix
symmetric_matrix = np.array([[0, 1, 2], [1, 0, 3], [2, 3, 0]])

# convert to igraph
G_symmetric = ig.Graph.Adjacency(symmetric_matrix.tolist())

# check if it is directed
print('G_symmetric is directed:', G_symmetric.is_directed())

# double check with is_directed() method from networkx package
G_symmetric_nx = nx.from_numpy_array(symmetric_matrix)
print('G_symmetric is directed:', nx.is_directed(G_symmetric_nx))
ntamas commented 6 months ago

ig.Graph.Adjacency() has a mode argument that specifies whether you want to create a directed graph or an undirected graph (see here). The default value of mode is "directed", which means that the returned graph will be directed. Hence, the is_directed() method returns the correct value. In contrast.

FWIW, NetworkX's from_numpy_array() function defaults to creating an undirected graph unless you specify otherwise with create_using=nx.DiGraph.

mcharrak commented 6 months ago

Thank you. Why would one want the default to be "directed" in the igraph implementation of this method?

What advantage does this have?

ntamas commented 6 months ago

Adjacency matrices can easily represent directed graphs. Making undirected the default would require a decision about how to handle the case when you pass a non-symmetric adjacency matrix to the function as this can be handled in multiple ways (see all the other options of the mode parameter of Graph.Adjacency). Making directed the default sidesteps the problem as all inputs are then completely valid.

szhorvat commented 6 months ago

You can ask the same question for a default of "undirected". What advantage does it have? What is more useful depends on your specific use case. Backwards compatibility is to be considered as well, so changing this at this point is not productive.

That said there is ongoing work to produce an entirely redesigned Python interface to igraph, which will hopefully be more consistent in its default choices, and therefore more predictable.

mcharrak commented 6 months ago

Thanks.

Given an adjacency matrix, how would you recommend using this library to determine whether the graph represented by the matrix is directed or undirected? From my understanding, the mode value needs to be set correctly when creating the graph. However, if the mode value is unknown, how can one use the is_directed() method properly to make this determination?

szhorvat commented 6 months ago

That's not really a meaningful question.

Here's an undirected graph:

image

Here's a directed graph:

image

They have the same adjacency matrix. It is up to you to determine how you want to interpret the matrix.

Of course, adjacency matrices of undirected graphs are symmetric, so if you want an undirected graph, you must supply a symmetric matrix.

mcharrak commented 6 months ago

Thanks. I understand the difference; however, I am starting to wonder what the point of the is_directed method is at all. How does this method work? Does it simply look up the format in which the igraph object is stored (D for directed, U for undirected) and return that?

szhorvat commented 6 months ago

I suggest you read up on the concept of directed and undirected graphs in a graph theory textbook. In the former, connections are directional, in the latter they are not.