|  |  | @@ -9,17 +9,37 @@ import scipy.sparse as sp | 
		
	
		
			
			|  |  |  | import torch | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def add_eye_sparse(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | def _check_tensor(adj_mat): | 
		
	
		
			
			|  |  |  | if not isinstance(adj_mat, torch.Tensor): | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a torch.Tensor') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def _check_sparse(adj_mat): | 
		
	
		
			
			|  |  |  | if not adj_mat.is_sparse: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be sparse') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def _check_dense(adj_mat): | 
		
	
		
			
			|  |  |  | if adj_mat.is_sparse: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be dense') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def _check_square(adj_mat): | 
		
	
		
			
			|  |  |  | if len(adj_mat.shape) != 2 or \ | 
		
	
		
			
			|  |  |  | adj_mat.shape[0] != adj_mat.shape[1]: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a square matrix') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def _check_2d(adj_mat): | 
		
	
		
			
			|  |  |  | if len(adj_mat.shape) != 2: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a square matrix') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def add_eye_sparse(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | _check_square(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | adj_mat = adj_mat.coalesce() | 
		
	
		
			
			|  |  |  | indices = adj_mat.indices() | 
		
	
		
			
			|  |  |  | values = adj_mat.values() | 
		
	
	
		
			
				|  |  | @@ -36,12 +56,42 @@ def add_eye_sparse(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | return adj_mat | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type_sparse(adj_mat): | 
		
	
		
			
			|  |  |  | if len(adj_mat.shape) != 2 or \ | 
		
	
		
			
			|  |  |  | adj_mat.shape[0] != adj_mat.shape[1]: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a square matrix') | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type_sparse(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | _check_square(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | adj_mat = add_eye_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | adj_mat = norm_adj_mat_two_node_types_sparse(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | return adj_mat | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type_dense(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_dense(adj_mat) | 
		
	
		
			
			|  |  |  | _check_square(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | adj_mat = adj_mat + torch.eye(adj_mat.shape[0], dtype=adj_mat.dtype) | 
		
	
		
			
			|  |  |  | adj_mat = norm_adj_mat_two_node_types_dense(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | return adj_mat | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_square(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | if adj_mat.is_sparse: | 
		
	
		
			
			|  |  |  | return norm_adj_mat_one_node_type_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | else: | 
		
	
		
			
			|  |  |  | return norm_adj_mat_one_node_type_dense(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_two_node_types_sparse(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | _check_2d(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | adj_mat = adj_mat.coalesce() | 
		
	
		
			
			|  |  |  | indices = adj_mat.indices() | 
		
	
	
		
			
				|  |  | @@ -50,28 +100,17 @@ def norm_adj_mat_one_node_type_sparse(adj_mat): | 
		
	
		
			
			|  |  |  | degrees_row = degrees_row.index_add(0, indices[0], values.to(degrees_row.dtype)) | 
		
	
		
			
			|  |  |  | degrees_col = torch.zeros(adj_mat.shape[1]) | 
		
	
		
			
			|  |  |  | degrees_col = degrees_col.index_add(0, indices[1], values.to(degrees_col.dtype)) | 
		
	
		
			
			|  |  |  | # degrees_row = torch.sqrt(degrees_row) | 
		
	
		
			
			|  |  |  | # degrees_col = torch.sqrt(degrees_col) | 
		
	
		
			
			|  |  |  | # print('degrees:', degrees) | 
		
	
		
			
			|  |  |  | # print('values:', values) | 
		
	
		
			
			|  |  |  | values = values.to(degrees_row.dtype) / torch.sqrt(degrees_row[indices[0]] * degrees_col[indices[1]]) | 
		
	
		
			
			|  |  |  | adj_mat = torch.sparse_coo_tensor(indices=indices, values=values, size=adj_mat.shape) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | return adj_mat | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type_dense(adj_mat): | 
		
	
		
			
			|  |  |  | if not isinstance(adj_mat, torch.Tensor): | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a torch.Tensor') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | if adj_mat.is_sparse: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be dense') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | if len(adj_mat.shape) != 2 or \ | 
		
	
		
			
			|  |  |  | adj_mat.shape[0] != adj_mat.shape[1]: | 
		
	
		
			
			|  |  |  | raise ValueError('adj_mat must be a square matrix') | 
		
	
		
			
			|  |  |  | def norm_adj_mat_two_node_types_dense(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_dense(adj_mat) | 
		
	
		
			
			|  |  |  | _check_2d(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | adj_mat = adj_mat + torch.eye(adj_mat.shape[0], dtype=adj_mat.dtype) | 
		
	
		
			
			|  |  |  | degrees_row = adj_mat.sum(1).view(-1, 1).to(torch.float32) | 
		
	
		
			
			|  |  |  | degrees_col = adj_mat.sum(0).view(1, -1).to(torch.float32) | 
		
	
		
			
			|  |  |  | degrees_row = torch.sqrt(degrees_row) | 
		
	
	
		
			
				|  |  | @@ -82,29 +121,11 @@ def norm_adj_mat_one_node_type_dense(adj_mat): | 
		
	
		
			
			|  |  |  | return adj_mat | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_one_node_type(adj_mat): | 
		
	
		
			
			|  |  |  | def norm_adj_mat_two_node_types(adj_mat: torch.Tensor) -> torch.Tensor: | 
		
	
		
			
			|  |  |  | _check_tensor(adj_mat) | 
		
	
		
			
			|  |  |  | _check_2d(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | if adj_mat.is_sparse: | 
		
	
		
			
			|  |  |  | return norm_adj_mat_one_node_type_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | return norm_adj_mat_two_node_types_sparse(adj_mat) | 
		
	
		
			
			|  |  |  | else: | 
		
	
		
			
			|  |  |  | return norm_adj_mat_one_node_type_dense(adj_mat) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | # def norm_adj_mat_one_node_type(adj): | 
		
	
		
			
			|  |  |  | #     adj = sp.coo_matrix(adj) | 
		
	
		
			
			|  |  |  | #     assert adj.shape[0] == adj.shape[1] | 
		
	
		
			
			|  |  |  | #     adj_ = adj + sp.eye(adj.shape[0]) | 
		
	
		
			
			|  |  |  | #     rowsum = np.array(adj_.sum(1)) | 
		
	
		
			
			|  |  |  | #     degree_mat_inv_sqrt = np.power(rowsum, -0.5).flatten() | 
		
	
		
			
			|  |  |  | #     degree_mat_inv_sqrt = sp.diags(degree_mat_inv_sqrt) | 
		
	
		
			
			|  |  |  | #     adj_normalized = adj_.dot(degree_mat_inv_sqrt).transpose().dot(degree_mat_inv_sqrt) | 
		
	
		
			
			|  |  |  | #     return adj_normalized | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def norm_adj_mat_two_node_types(adj): | 
		
	
		
			
			|  |  |  | adj = sp.coo_matrix(adj) | 
		
	
		
			
			|  |  |  | rowsum = np.array(adj.sum(1)) | 
		
	
		
			
			|  |  |  | colsum = np.array(adj.sum(0)) | 
		
	
		
			
			|  |  |  | rowdegree_mat_inv = sp.diags(np.nan_to_num(np.power(rowsum, -0.5)).flatten()) | 
		
	
		
			
			|  |  |  | coldegree_mat_inv = sp.diags(np.nan_to_num(np.power(colsum, -0.5)).flatten()) | 
		
	
		
			
			|  |  |  | adj_normalized = rowdegree_mat_inv.dot(adj).dot(coldegree_mat_inv).tocoo() | 
		
	
		
			
			|  |  |  | return adj_normalized | 
		
	
		
			
			|  |  |  | return norm_adj_mat_two_node_types_dense(adj_mat) |