Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __pycache__/

# C extensions
*.so
*.md

# Distribution / packaging
.Python
Expand Down
229 changes: 16 additions & 213 deletions evaluation_function/algorithms/__init__.py
Original file line number Diff line number Diff line change
@@ -1,213 +1,16 @@
"""
Graph Theory Algorithms

This package contains implementations of various graph algorithms.

Modules:
- utils: Common helper functions and data structures
- coloring: Graph coloring algorithms (greedy, DSatur, chromatic number)
- mst: Minimum spanning tree algorithms (Kruskal, Prim, verification)
- path: Eulerian and Hamiltonian path/circuit algorithms
"""

from .utils import (
# Union-Find data structure
UnionFind,

# Adjacency builders
build_adjacency_list,
build_adjacency_list_weighted,
build_adjacency_multiset,
build_edge_set,

# Degree calculations
get_degree,
get_in_out_degree,

# Connectivity
is_connected,
is_weakly_connected,
count_components,

# Edge utilities
get_edge_weight,
)

from .coloring import (
# Verification
verify_vertex_coloring,
verify_edge_coloring,
detect_coloring_conflicts,
detect_edge_coloring_conflicts,

# Coloring algorithms
greedy_coloring,
dsatur_coloring,
greedy_edge_coloring,

# Chromatic number
compute_chromatic_number,
compute_chromatic_index,

# Helper functions (coloring-specific)
build_line_graph_adjacency,
)

from .mst import (
# MST algorithms
kruskal_mst,
prim_mst,
compute_mst,

# MST verification
verify_spanning_tree,
verify_mst,
verify_mst_edges,

# Disconnected graph handling
compute_minimum_spanning_forest,

# Visualization support
get_mst_visualization,
get_mst_animation_steps,

# High-level API
find_mst,
evaluate_mst_submission,

# MST-specific helper (wrapper)
is_graph_connected,
)

from .path import (
# Eulerian existence checks
check_eulerian_undirected,
check_eulerian_directed,
check_eulerian_existence,

# Eulerian path/circuit finding
find_eulerian_path_undirected,
find_eulerian_path_directed,
find_eulerian_path,
find_eulerian_circuit,

# Eulerian verification
verify_eulerian_path,

# Hamiltonian verification
verify_hamiltonian_path,

# Hamiltonian existence
find_hamiltonian_path_backtrack,
check_hamiltonian_existence,

# High-level API
evaluate_eulerian_path,
evaluate_hamiltonian_path,

# Feedback
get_eulerian_feedback,
get_hamiltonian_feedback,

# Path-specific wrappers
is_connected_undirected,
is_weakly_connected_directed,
)

__all__ = [
# Utils - Union-Find
"UnionFind",

# Utils - Adjacency builders
"build_adjacency_list",
"build_adjacency_list_weighted",
"build_adjacency_multiset",
"build_edge_set",

# Utils - Degree calculations
"get_degree",
"get_in_out_degree",

# Utils - Connectivity
"is_connected",
"is_weakly_connected",
"count_components",

# Utils - Edge utilities
"get_edge_weight",

# Coloring - Verification
"verify_vertex_coloring",
"verify_edge_coloring",
"detect_coloring_conflicts",
"detect_edge_coloring_conflicts",

# Coloring algorithms
"greedy_coloring",
"dsatur_coloring",
"greedy_edge_coloring",

# Chromatic number
"compute_chromatic_number",
"compute_chromatic_index",

# Coloring - Helper functions
"build_line_graph_adjacency",

# MST algorithms
"kruskal_mst",
"prim_mst",
"compute_mst",

# MST verification
"verify_spanning_tree",
"verify_mst",
"verify_mst_edges",

# MST - Disconnected graph handling
"compute_minimum_spanning_forest",

# MST - Visualization support
"get_mst_visualization",
"get_mst_animation_steps",

# MST - High-level API
"find_mst",
"evaluate_mst_submission",

# MST - Wrapper
"is_graph_connected",

# Path - Eulerian existence checks
"check_eulerian_undirected",
"check_eulerian_directed",
"check_eulerian_existence",

# Path - Eulerian path/circuit finding
"find_eulerian_path_undirected",
"find_eulerian_path_directed",
"find_eulerian_path",
"find_eulerian_circuit",

# Path - Eulerian verification
"verify_eulerian_path",

# Path - Hamiltonian verification
"verify_hamiltonian_path",

# Path - Hamiltonian existence
"find_hamiltonian_path_backtrack",
"check_hamiltonian_existence",

# Path - High-level API
"evaluate_eulerian_path",
"evaluate_hamiltonian_path",

# Path - Feedback
"get_eulerian_feedback",
"get_hamiltonian_feedback",

# Path - Wrappers
"is_connected_undirected",
"is_weakly_connected_directed",
]
"""
Core graph algorithms used by the evaluation function.
"""

from .connectivity import connectivity_info
from .shortest_path import shortest_path_info
from .bipartite import bipartite_info
from .cycles import cycle_info

__all__ = [
"connectivity_info",
"shortest_path_info",
"bipartite_info",
"cycle_info",
]

84 changes: 84 additions & 0 deletions evaluation_function/algorithms/bipartite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations

from collections import deque

from evaluation_function.schemas import BipartiteResult, Graph

from .utils import build_adjacency, node_ids


def _reconstruct_odd_cycle(u: str, v: str, parent: dict[str, str], depth: dict[str, int]) -> list[str]:
# Build paths to root
pu = [u]
pv = [v]
cu, cv = u, v
while cu in parent:
cu = parent[cu]
pu.append(cu)
while cv in parent:
cv = parent[cv]
pv.append(cv)

set_pu = {x: i for i, x in enumerate(pu)}
lca = None
j = None
for idx, node in enumerate(pv):
if node in set_pu:
lca = node
j = idx
break

if lca is None or j is None:
# Fallback: just return the triangle-ish evidence
return [u, v, u]

i = set_pu[lca]
path_u_to_lca = pu[: i + 1] # u..lca
path_v_to_lca = pv[: j + 1] # v..lca
path_v_to_lca.reverse() # lca..v

cycle = path_u_to_lca + path_v_to_lca[1:] + [u]
return cycle


def bipartite_info(
graph: Graph,
*,
return_partitions: bool = False,
return_odd_cycle: bool = False,
) -> BipartiteResult:
# Bipartite is typically defined for undirected graphs; we treat directed as undirected for checking.
adj = build_adjacency(graph, undirected=True)

color: dict[str, int] = {}
parent: dict[str, str] = {}
depth: dict[str, int] = {}

for start in node_ids(graph):
if start in color:
continue
q = deque([start])
color[start] = 0
depth[start] = 0

while q:
u = q.popleft()
for ae in adj.get(u, []):
v = ae.to
if v not in color:
color[v] = 1 - color[u]
parent[v] = u
depth[v] = depth[u] + 1
q.append(v)
elif color[v] == color[u]:
cycle = _reconstruct_odd_cycle(u, v, parent, depth) if return_odd_cycle else None
return BipartiteResult(is_bipartite=False, partitions=None, odd_cycle=cycle)

partitions = None
if return_partitions:
left = [n for n in color.keys() if color[n] == 0]
right = [n for n in color.keys() if color[n] == 1]
partitions = [left, right]

return BipartiteResult(is_bipartite=True, partitions=partitions, odd_cycle=None)

Loading
Loading