mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
* fix(kg): preserve isolated nodes in graph analytics * fix(kg): support node fallbacks and community payloads ---------
113 lines
3.4 KiB
Python
113 lines
3.4 KiB
Python
"""Regression tests for KG analytics node scope handling."""
|
|
|
|
import networkx as nx
|
|
|
|
from semantica.kg.centrality_calculator import CentralityCalculator
|
|
from semantica.kg.community_detector import CommunityDetector
|
|
from semantica.kg.connectivity_analyzer import ConnectivityAnalyzer
|
|
|
|
|
|
def _graph_with_isolated_node():
|
|
return {
|
|
"entities": [{"id": "A"}, {"id": "B"}, {"id": "C"}],
|
|
"relationships": [{"source": "A", "target": "B"}],
|
|
}
|
|
|
|
|
|
def test_centrality_keeps_declared_isolated_nodes():
|
|
result = CentralityCalculator().calculate_degree_centrality(
|
|
_graph_with_isolated_node()
|
|
)
|
|
|
|
assert result["total_nodes"] == 3
|
|
assert result["centrality"]["C"] == 0.0
|
|
|
|
|
|
def test_connectivity_reports_declared_isolated_nodes():
|
|
result = ConnectivityAnalyzer().analyze_connectivity(
|
|
_graph_with_isolated_node()
|
|
)
|
|
|
|
assert result["num_nodes"] == 3
|
|
assert result["num_components"] == 2
|
|
assert ["C"] in result["components"]
|
|
assert result["is_connected"] is False
|
|
|
|
|
|
def test_community_detection_keeps_declared_isolated_nodes():
|
|
detector = CommunityDetector()
|
|
result = detector.detect_communities(_graph_with_isolated_node())
|
|
|
|
assert set(result["node_assignments"]) == {"A", "B", "C"}
|
|
metrics = detector.calculate_community_metrics(
|
|
_graph_with_isolated_node(), result
|
|
)
|
|
assert metrics["num_communities"] == 2
|
|
structure = detector.analyze_community_structure(
|
|
_graph_with_isolated_node(), result
|
|
)
|
|
assert structure["num_communities"] == 2
|
|
|
|
|
|
def test_community_detection_returns_singletons_for_edgeless_graph():
|
|
graph = {"entities": [{"id": "A"}, {"id": "B"}], "relationships": []}
|
|
|
|
result = CommunityDetector().detect_communities(graph)
|
|
|
|
assert {frozenset(community) for community in result["communities"]} == {
|
|
frozenset({"A"}),
|
|
frozenset({"B"}),
|
|
}
|
|
|
|
|
|
def test_networkx_graph_keeps_isolated_nodes_for_analytics():
|
|
graph = nx.Graph()
|
|
graph.add_nodes_from(["A", "B", "C"])
|
|
graph.add_edge("A", "B")
|
|
|
|
centrality = CentralityCalculator().calculate_degree_centrality(graph)
|
|
connectivity = ConnectivityAnalyzer().analyze_connectivity(graph)
|
|
|
|
assert centrality["total_nodes"] == 3
|
|
assert centrality["centrality"]["C"] == 0.0
|
|
assert connectivity["num_nodes"] == 3
|
|
assert connectivity["num_components"] == 2
|
|
|
|
|
|
def test_nodes_edges_payload_keeps_declared_isolated_nodes():
|
|
graph = {
|
|
"nodes": [{"id": "A"}, {"id": "B"}, {"id": "C"}],
|
|
"edges": [("A", "B")],
|
|
}
|
|
|
|
result = CentralityCalculator().calculate_degree_centrality(graph)
|
|
|
|
assert result["total_nodes"] == 3
|
|
assert result["centrality"]["C"] == 0.0
|
|
|
|
|
|
def test_name_and_text_nodes_are_kept_when_ids_are_missing():
|
|
graph = {
|
|
"entities": [{"name": "Alice"}, {"text": "Bob"}],
|
|
"relationships": [],
|
|
}
|
|
|
|
result = CentralityCalculator().calculate_degree_centrality(graph)
|
|
|
|
assert result["total_nodes"] == 2
|
|
assert set(result["centrality"]) == {"Alice", "Bob"}
|
|
|
|
|
|
def test_community_metrics_accepts_communities_payload():
|
|
detector = CommunityDetector()
|
|
graph = {
|
|
"entities": [{"id": "A"}, {"id": "B"}, {"id": "C"}],
|
|
"relationships": [{"source": "A", "target": "B"}],
|
|
}
|
|
result = {"communities": [["A", "B"], ["C"]]}
|
|
|
|
metrics = detector.calculate_community_metrics(graph, result)
|
|
|
|
assert metrics["num_communities"] == 2
|
|
assert metrics["community_sizes"] == {0: 2, 1: 1}
|