156 lines
4.7 KiB
Python
156 lines
4.7 KiB
Python
|
|
"""
|
|||
|
|
图算法服务
|
|||
|
|
提供图的最短路径等算法服务
|
|||
|
|
"""
|
|||
|
|
from typing import List, Optional, Tuple
|
|||
|
|
|
|||
|
|
from models.mapping_graph import MappingGraph, GraphEdge
|
|||
|
|
from utils.graph_algorithms import GraphAlgorithms
|
|||
|
|
from utils.logger import get_logger
|
|||
|
|
|
|||
|
|
logger = get_logger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GraphService:
|
|||
|
|
"""图算法服务类"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
self.algorithms = GraphAlgorithms()
|
|||
|
|
|
|||
|
|
def find_shortest_path(self, graph: MappingGraph,
|
|||
|
|
source_id: int,
|
|||
|
|
target_id: int,
|
|||
|
|
use_weight: bool = True) -> Optional[Tuple[List[int], float]]:
|
|||
|
|
"""
|
|||
|
|
查找最短路径
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
graph: 映射图
|
|||
|
|
source_id: 源节点ID
|
|||
|
|
target_id: 目标节点ID
|
|||
|
|
use_weight: 是否使用权重(True使用Dijkstra,False使用BFS)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
(路径节点ID列表, 总权重) 或 None
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
if use_weight:
|
|||
|
|
result = self.algorithms.dijkstra_shortest_path(graph, source_id, target_id)
|
|||
|
|
if result:
|
|||
|
|
path, weight = result
|
|||
|
|
logger.info(f"Found shortest path (Dijkstra): {path}, weight: {weight}")
|
|||
|
|
return path, weight
|
|||
|
|
else:
|
|||
|
|
path = self.algorithms.bfs_shortest_path(graph, source_id, target_id)
|
|||
|
|
if path:
|
|||
|
|
logger.info(f"Found shortest path (BFS): {path}")
|
|||
|
|
return path, 0.0
|
|||
|
|
|
|||
|
|
return None
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to find shortest path: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def find_all_paths(self, graph: MappingGraph,
|
|||
|
|
source_id: int,
|
|||
|
|
target_id: int,
|
|||
|
|
max_length: int = 10) -> List[List[int]]:
|
|||
|
|
"""
|
|||
|
|
查找所有路径
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
graph: 映射图
|
|||
|
|
source_id: 源节点ID
|
|||
|
|
target_id: 目标节点ID
|
|||
|
|
max_length: 最大路径长度
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
所有路径列表
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
paths = self.algorithms.find_all_paths(graph, source_id, target_id, max_length)
|
|||
|
|
logger.info(f"Found {len(paths)} paths from {source_id} to {target_id}")
|
|||
|
|
return paths
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to find all paths: {e}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def get_path_mappings(self, graph: MappingGraph,
|
|||
|
|
path: List[int]) -> List[GraphEdge]:
|
|||
|
|
"""
|
|||
|
|
获取路径上的所有映射关系
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
graph: 映射图
|
|||
|
|
path: 路径节点ID列表
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
边(映射关系)列表
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
edges = self.algorithms.get_path_edges(graph, path)
|
|||
|
|
logger.info(f"Got {len(edges)} mappings for path")
|
|||
|
|
return edges
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to get path mappings: {e}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def detect_cycles(self, graph: MappingGraph) -> List[List[int]]:
|
|||
|
|
"""
|
|||
|
|
检测图中的环
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
graph: 映射图
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
所有环的列表
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
cycles = self.algorithms.detect_cycles(graph)
|
|||
|
|
if cycles:
|
|||
|
|
logger.warning(f"Detected {len(cycles)} cycles in graph")
|
|||
|
|
return cycles
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Failed to detect cycles: {e}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def validate_graph(self, graph: MappingGraph) -> Tuple[bool, List[str]]:
|
|||
|
|
"""
|
|||
|
|
验证图的有效性
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
graph: 映射图
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
(是否有效, 错误信息列表)
|
|||
|
|
"""
|
|||
|
|
errors = []
|
|||
|
|
|
|||
|
|
# 检查孤立节点
|
|||
|
|
isolated_nodes = []
|
|||
|
|
for node_id in graph.nodes:
|
|||
|
|
if not graph.get_edges_from(node_id) and not graph.get_edges_to(node_id):
|
|||
|
|
isolated_nodes.append(node_id)
|
|||
|
|
|
|||
|
|
if isolated_nodes:
|
|||
|
|
errors.append(f"发现 {len(isolated_nodes)} 个孤立节点")
|
|||
|
|
|
|||
|
|
# 检查环
|
|||
|
|
cycles = self.detect_cycles(graph)
|
|||
|
|
if cycles:
|
|||
|
|
errors.append(f"发现 {len(cycles)} 个环路")
|
|||
|
|
|
|||
|
|
# 检查边的有效性
|
|||
|
|
invalid_edges = []
|
|||
|
|
for edge in graph.edges:
|
|||
|
|
if edge.source_field_id not in graph.nodes:
|
|||
|
|
invalid_edges.append(f"边 {edge.id} 的源节点不存在")
|
|||
|
|
if edge.target_field_id not in graph.nodes:
|
|||
|
|
invalid_edges.append(f"边 {edge.id} 的目标节点不存在")
|
|||
|
|
|
|||
|
|
if invalid_edges:
|
|||
|
|
errors.extend(invalid_edges)
|
|||
|
|
|
|||
|
|
is_valid = len(errors) == 0
|
|||
|
|
return is_valid, errors
|