Constrói a matriz de adjacência normalizada à = D^(-1/2) · W · D^(-1/2)
para um grafo bipartido user↔item, no formato esperado pelo LightGCN.
Estrutura de W
W = [ 0 R ] shape (M+N) × (M+N)
[ R^T 0 ]onde M = número de usuários, N = número de itens, e R[u,i] é o peso agregado das interações entre o usuário u e o item i. Quando múltiplas arestas conectam o mesmo par (ex.: like + comentário), os pesos são somados — interpretação: cada interação positiva adiciona evidência ao sinal user↔item.
Particionamento por user_prefix
O lado "usuário" do grafo é identificado pelo prefixo dos external IDs.
Exemplo: com user_prefix: "profile:", todos os vértices "profile:*"
são classificados como usuários e o restante como itens.
Reindexação
IDs internos do IdMap podem ter gaps (após pruning ou inserções
intercaladas). Esta função reindexa todos os usuários para o intervalo
[0, M-1] e todos os itens para [M, M+N-1] na matriz, mantendo um
mapa internal_id → row_index para uso na inferência.
Limites
Materializa a matriz como tensor denso. Adequado para grafos com até ~50k vértices. Para grafos maiores, usar representação sparse (planejado para v0.3).
Summary
Functions
Constrói à para o grafo identificado por conf usando user_prefix
para separar os dois lados do grafo bipartido.
Types
@type build_result() :: %{ adj_norm: Nx.Tensor.t(), user_index: index_map(), item_index: index_map(), positive_pairs: [{non_neg_integer(), non_neg_integer()}], user_count: non_neg_integer(), item_count: non_neg_integer(), node_count: non_neg_integer() }
@type index_map() :: %{required(non_neg_integer()) => non_neg_integer()}
Functions
@spec build(MeliGraph.Config.t(), String.t()) :: {:ok, build_result()} | {:error, :empty_graph}
Constrói à para o grafo identificado por conf usando user_prefix
para separar os dois lados do grafo bipartido.
O resultado também inclui positive_pairs, a lista deduplicada de
pares (user_row, item_row) representando interações observadas —
usada pelo Trainer para amostrar mini-batches BPR.
Retorna {:error, :empty_graph} quando não há usuários, itens ou
arestas user↔item após o particionamento.