Я хочу получить список смежности с помощью pytorch geo. Есть ли какой-нибудь метод в pytorch для преобразования матрицы смежности, но оптимизированный, а не циклический?

Я хочу получить список смежности с помощью pytorch geo. Есть ли в pytorch какой-нибудь метод для преобразования матрицы смежности, но оптимизированный, а не циклический?

Так :

import numpy as np
import networkx as nx


def adjacency_matrix_to_list(adj_matrix):
    # Créer un graphe à partir de la matrice d'adjacence
    graph = nx.from_numpy_matrix(adj_matrix)

    # Convertir le graphe en liste d'adjacence
    adj_list = nx.to_dict_of_lists(graph)

    return adj_list
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
97
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Под отказом от использования циклов я предполагаю, что вы имеете в виду отказ от таких вложенных циклов:

import torch

def adjacency_matrix_to_list_with_loops(adj_matrix):
    num_nodes = adj_matrix.shape[0]
    adj_list = []
    for i in range(num_nodes):
        neighbors = []
        for j in range(num_nodes):
            if adj_matrix[i][j] == 1:
                neighbors.append(j)
        adj_list.append(neighbors)
    return adj_list

Если вы не хотите использовать дополнительную библиотеку, например networkx, вам нужно будет использовать хотя бы один цикл для перебора узлов матрицы.

Результатом torch.nonzero является тензор формы (n, 1) (где n — количество ненулевых элементов). Метод .squeeze() удаляет одноэлементное измерение, в результате чего получается тензор формы (n,).

import torch

def adjacency_matrix_to_list(adj_matrix):
    num_nodes = adj_matrix.shape[0]
    adj_list = []
    for i in range(num_nodes):
        # Find indices of non-zero elements in the i-th row
        neighbors = torch.nonzero(adj_matrix[i]).squeeze()
        adj_list.append(neighbors.tolist())
    return adj_list

Производительность этого метода будет зависеть от размера графа и разреженности матрицы смежности. Для очень больших графов решение networkx может оказаться более оптимизированным.

Вы не можете избежать использования цикла. В лучшем случае вы можете выполнить цикл на более низком уровне, который работает быстрее, чем стандартный Python.

Вы можете использовать torch.nonzero, чтобы найти ненулевые значения и перебрать их для построения выходного словаря.

import torch
from collections import defaultdict

# ie init random adjacency matrix
adj_matrix = (torch.rand(10,10)>0.8).float()

# grab nonzero values
nonzero_vals = torch.nonzero(adj_matrix)

# init output adjacency list
adj_list = defaultdict(set)

# add nonzero rows/cols to output dict 
for (row, col) in nonzero_vals.tolist():
    adj_list[row].update([col])
    if row != col:
        adj_list[col].update([row])
    
adj_list = {k:list(v) for k,v in adj_list.items()}
Ответ принят как подходящий

Нет прямого пути. Пожалуйста, используйте следующие методы.

Использование torch.tensor

Метод 1:

import torch
from scipy.sparse import csr_matrix
import torch
from numpy import nonzero
import networkx as nx
import matplotlib.pyplot as plt


adj_matrix = torch.tensor([[0, 1, 0],
                           [1, 0, 1],
                           [0, 1, 0]])

# Convert adjacency matrix to adjacency list
src_indices, dst_indices = adj_matrix.nonzero().unbind(-1)
"""
split_sizes : it tells us how many outgoing edges (neighbors) each node has in the graph.
minlength=adj_matrix.size(0) => the same length as the number of nodes in the graph.
torch.bincount: This function is used to count the number of occurrences 
of each element in the input tensor src_indices.

It counts the occurrences of each unique value in src_indices.
"""
split_sizes = torch.bincount(input=src_indices, minlength=adj_matrix.size(0))
"""
counts the occurrences of each unique value in src_indices
"""
adj_list = torch.split(dst_indices, split_sizes.tolist())

# Convert adjacency list tensors to regular lists
res = [x.tolist() for x in adj_list]
print(res)#[[1], [0, 2], [1]]

Метод 2:

from scipy.sparse import csr_matrix
from numpy import nonzero
import torch

adj_matrix = torch.tensor([[0, 1, 0],
                           [1, 0, 1],
                           [0, 1, 0]])

mask = (adj_matrix != 0)
source_indices  = mask.nonzero(as_tuple=True)[0]
print(source_indices )
destination_indices = mask.nonzero(as_tuple = True)[1]
print(destination_indices)
"""
It holds a tensor where each row represents an edge in the graph. 
The first element in each row is the source node index, 
and the second element is the destination node index.
dim=1: This argument specifies that the tensors should be stacked along 
the first dimension (columns in this case)
"""
edge_index = torch.stack(tensors = (source_indices,destination_indices), dim=1)
print(edge_index)
"""
tensor([[0, 1],
        [1, 0],
        [1, 2],
        [2, 1]])
"""
"""
.sum(dim=1): This performs a summation along dimension 1 (columns) for each row.
It counts the number of True values (non-zero elements) in each row.
adj_list  : Stores the number of outgoing edges (neighbors) for each node in the graph. 
"""
adj_list = (adj_matrix != 0 ).sum(dim=1).tolist()
print(adj_list)#[1, 2, 1]
adj_list = [[] for _ in adj_list]
print(adj_list)#[[], [], []]
"""
src: Represents the source node index (the node where the edge originates).
dst: Represents the destination node index (the node where the edge points to)
"""

# Scatter destination nodes into their corresponding lists
for i, (src, dst) in enumerate(edge_index):
    adj_list[src.item()].append(dst.item())  # Append destination node directly

print(adj_list)#[[1], [0, 2], [1]]
    

Без использования torch.tensor

import numpy as np
from scipy.sparse import csr_matrix 

# Example with sparse CSR matrix
sparse_adj_matrix = csr_matrix([[0, 1, 0],
                                [1, 0, 1],
                                [0, 1, 0]])

def adjacency_matrix_to_list(adj_matrix):
    if not isinstance(adj_matrix, csr_matrix):
    # Convert dense matrix to sparse CSR format for potential efficiency gains
     adj_matrix = csr_matrix(adj_matrix)

    num_nodes = adj_matrix.shape[0]
    print('num_nodes :', num_nodes) #3
    
    # Extract neighboring nodes using CSR matrix attributes
    split_indices = np.split(adj_matrix.indices,adj_matrix.indptr[1:-1] )
    print('split_indices :',split_indices) #split_indices : [array([1]), array([0, 2]), array([1])]

    # Convert split_indices to a list of lists
    adj_list = [arr.tolist() for arr in split_indices]

    return adj_list

adj_list = adjacency_matrix_to_list(sparse_adj_matrix)
print(adj_list)  # Output: [[1], [0, 2], [1]]

Или менее эффективный

adj_matrix = np.array([[0, 1, 0],
                       [1, 0, 1],
                       [0, 1, 0]])


adj_list = [list(np.where(row)[0]) for row in adj_matrix]

print(adj_list)  # Output: [[1], [0, 2], [1]]

Другие вопросы по теме