1

I came across the why-laplacian-matrix-need-normalization-and-how-come-the-sqrt-of-degree-matrix.

I have a weighted adjacency matrix adjm (file data.csv on Google Drive). The matrix adjm is non-symmetric. I have built the directed graph graph and then remove loops and multiple edges. The resulted graph is connected and simple. I have used the graph.laplacian() function from the igraph package. I expected to obtain the symmetric matrix, but the matrix L_matrix is not symmetric.

library(igraph)

# read from file    
adjm = as.matrix(read.csv("data.csv", sep=",", row.names = 1))

isSymmetric(adjm) # FALSE

graph <- graph_from_adjacency_matrix(adjm, weighted=TRUE)

table(count_multiple(graph))

# remove loops and multiple edges
graph <- simplify(graph) 

is_connected(graph) # TRUE

L_matrix <- graph.laplacian(graph, norm=TRUE, 
                            weights = E(graph)$weight, 
                            sparse=FALSE)

isSymmetric(L_matrix) # FALSE

Edit. I tried to vary a tolerance tol from 0.1 to 0.0001 but the result is FALSE.

isSymmetric(L_matrix, tol = 0.01) # FALSE

The L_matrix is square 104 by 104 matrix. I found the difference between the first row and the first column. Then I calculated the number of zeros, it is less than 104.

test0 <- L_matrix[1,] - L_matrix[,1]
test0 <- test0[test0 == 0]
length(test0[test0 == 0])
[1] 90

Edit 2.

I want to make a spectral clustering.

Question. Why is the Laplacian Matrix not symmetric?

Nick
  • 1,086
  • 7
  • 21

1 Answers1

1

"The resulted graph is connected and simple" isn't right: the graph is still directed and the adjacency matrix is not symmetric. E.g.,

as_adjacency_matrix(graph)[1:5, 1:5]
# 5 x 5 sparse Matrix of class "dgCMatrix"
#      dddd dddD ddDd ddDD dDdd
# dddd    .    .    .    .    .
# dddD    .    .    .    .    .
# ddDd    .    .    .    .    .
# ddDD    .    .    .    .    .
# dDdd    .    1    .    .    .

So, as a result, the Laplacian isn't symmetric either.

Edit: as to make the graph undirected, we may use

adjm <- pmax(adjm, t(adjm))
all(adjm == t(adjm))
# [1] TRUE

In this way both elements (i,j) and (j,i) are replaced by the larger of the two. Interestingly, it still doesn't make the Laplacian symmetric:

L_matrix[1:5, 1:5]
#      dddd        dddD ddDd ddDD       dDdd
# dddd    1  0.00000000    0    0  0.0000000
# dddD    0  1.00000000    0    0 -0.0703125
# ddDd    0  0.00000000    1    0  0.0000000
# ddDD    0  0.00000000    0    1  0.0000000
# dDdd    0 -0.06575342    0    0  1.0000000

The issue is in how normalized = TRUE works (seems like a bug; at least it contradicts the documentation). Doing the normalization step manually we have

L_matrix <- graph.laplacian(graph, norm = FALSE, 
                            weights = E(graph)$weight,
                            sparse = FALSE)
L_matrix <- diag(1 / sqrt(diag(L_matrix))) %*% L_matrix %*% diag(1 / sqrt(diag(L_matrix)))
isSymmetric(L_matrix)
# [1] TRUE
L_matrix[1:5, 1:5]
#      [,1]        [,2] [,3] [,4]        [,5]
# [1,]    1  0.00000000    0    0  0.00000000
# [2,]    0  1.00000000    0    0 -0.06799476
# [3,]    0  0.00000000    1    0  0.00000000
# [4,]    0  0.00000000    0    1  0.00000000
# [5,]    0 -0.06799476    0    0  1.00000000
Julius Vainora
  • 47,421
  • 9
  • 90
  • 102
  • Is it exist a function in the igraph package to make a symmetric matrix? – Nick Apr 14 '19 at 10:32
  • It's not about existing, but about what you actually want to achieve. How do you want to symmetrize a matrix? Do you want for an undirected edge to exist if both directed ones are present? Or is just one enough? – Julius Vainora Apr 14 '19 at 10:34
  • I want to make a spectral clustering. I am looking for mutual k-nearest neighbors. But I think one edge is enough. – Nick Apr 14 '19 at 10:48
  • Thank you for the answer. Why do you use the the command all(adjm == t(adjm)) instead of isSymmetric(adjm)? Because diagonal elements of adjm are not zeros? – Nick Apr 15 '19 at 04:40
  • 1
    @Nick, because `isSymmetric(adjm)` give `FALSE`, which clearly is wrong, and I didn't want to look for details why, but now I checked and see that `isSymmetric` for symmetry requires column names and row names to be the same as well. `adjm` doesn't satisfy that and returns `FALSE` just for this reason. After fixing row names it does return `TRUE` as well. – Julius Vainora Apr 15 '19 at 09:20