diff --git a/python/datastruct.py b/python/datastruct.py index f543988..8530c04 100644 --- a/python/datastruct.py +++ b/python/datastruct.py @@ -162,7 +162,9 @@ class UnionFindQueue(Generic[_NameT, _ElemT]): assert node is not None return node.min_node.data - def merge(self, sub_queues: "list[UnionFindQueue[_NameT, _ElemT]]") -> None: + def merge(self, + sub_queues: "list[UnionFindQueue[_NameT, _ElemT]]" + ) -> None: """Merge the specified queues. This queue must inititially be empty. @@ -171,7 +173,8 @@ class UnionFindQueue(Generic[_NameT, _ElemT]): This function removes all elements from the specified sub-queues and adds them to this queue. - After merging, this queue retains a reference to the list of sub-queues. + After merging, this queue retains a reference to the list of + sub-queues. This function takes time O(len(sub_queues) * log(n)). """ @@ -268,7 +271,9 @@ class UnionFindQueue(Generic[_NameT, _ElemT]): min_node = right_min_node node.min_node = min_node - def _rotate_left(self, node: Node[_NameT, _ElemT]) -> Node[_NameT, _ElemT]: + def _rotate_left(self, + node: Node[_NameT, _ElemT] + ) -> Node[_NameT, _ElemT]: """Rotate the specified subtree to the left. Return the new root node of the subtree. @@ -303,7 +308,9 @@ class UnionFindQueue(Generic[_NameT, _ElemT]): return new_top - def _rotate_right(self, node: Node[_NameT, _ElemT]) -> Node[_NameT, _ElemT]: + def _rotate_right(self, + node: Node[_NameT, _ElemT] + ) -> Node[_NameT, _ElemT]: """Rotate the specified node to the right. Return the new root node of the subtree. @@ -338,7 +345,9 @@ class UnionFindQueue(Generic[_NameT, _ElemT]): return new_top - def _rebalance_up(self, node: Node[_NameT, _ElemT]) -> Node[_NameT, _ElemT]: + def _rebalance_up(self, + node: Node[_NameT, _ElemT] + ) -> Node[_NameT, _ElemT]: """Repair and rebalance the specified node and its ancestors. Return the root node of the rebalanced tree. diff --git a/run_checks.sh b/run_checks.sh index c751e85..24a0fed 100755 --- a/run_checks.sh +++ b/run_checks.sh @@ -1,14 +1,18 @@ -#!/bin/sh +#!/bin/bash set -e +echo +echo "Running pycodestyle" +pycodestyle python/mwmatching.py python/datastruct.py tests + echo echo "Running mypy" mypy --disallow-incomplete-defs python tests echo echo "Running pylint" -pylint --ignore=test_mwmatching.py python tests +pylint --ignore=test_mwmatching.py python tests || [ $(($? & 3)) -eq 0 ] echo echo "Running test_mwmatching.py" diff --git a/tests/generate/hardcard.py b/tests/generate/hardcard.py index da897f5..cccf5db 100644 --- a/tests/generate/hardcard.py +++ b/tests/generate/hardcard.py @@ -7,7 +7,7 @@ described by Gabow. Reference: H. N. Gabow, "An efficient implementation of Edmonds' algorithm for maximum matching on graphs", JACM 23 (1976), pp. 221-234. - + Based on Fortran program "hardcard.f" by R. Bruce Mattingly, 1991. Rewritten in Python by Joris van Rantwijk, 2023. diff --git a/tests/generate/make_random_graph.py b/tests/generate/make_random_graph.py index 18e4339..f6848a3 100644 --- a/tests/generate/make_random_graph.py +++ b/tests/generate/make_random_graph.py @@ -14,7 +14,7 @@ from typing import TextIO def write_dimacs_graph( f: TextIO, - edges: list[tuple[int, int, int|float]] + edges: list[tuple[int, int, float]] ) -> None: """Write a graph in DIMACS edge list format.""" @@ -38,7 +38,7 @@ def make_random_graph( max_weight: float, float_weights: bool, rng: random.Random - ) -> list[tuple[int, int, int|float]]: + ) -> list[tuple[int, int, float]]: """Generate a random graph with random edge weights.""" edge_set: set[tuple[int, int]] = set() @@ -59,9 +59,9 @@ def make_random_graph( rng.shuffle(edge_candidates) edge_set.update(edge_candidates[:m]) - edges: list[tuple[int, int, int|float]] = [] + edges: list[tuple[int, int, float]] = [] for (x, y) in sorted(edge_set): - w: int|float + w: float if float_weights: w = rng.uniform(1.0e-8, max_weight) else: diff --git a/tests/generate/make_slow_graph.py b/tests/generate/make_slow_graph.py index b0b2b86..541685b 100644 --- a/tests/generate/make_slow_graph.py +++ b/tests/generate/make_slow_graph.py @@ -36,7 +36,8 @@ def patch_matching_code() -> None: ret = orig_substage_calc_dual_delta(*args, **kwargs) return ret - mwmatching._MatchingContext.make_blossom = stub_make_blossom # type: ignore + mwmatching._MatchingContext.make_blossom = ( # type: ignore + stub_make_blossom) mwmatching._MatchingContext.substage_calc_dual_delta = ( # type: ignore stub_substage_calc_dual_delta) diff --git a/tests/run_test.py b/tests/run_test.py index 6453641..66ef7d0 100755 --- a/tests/run_test.py +++ b/tests/run_test.py @@ -29,7 +29,7 @@ class SolverError(Exception): class Graph(NamedTuple): """Represents a graph. Vertex indices start from 0.""" - edges: list[tuple[int, int, int|float]] + edges: list[tuple[int, int, float]] def num_vertex(self) -> int: """Count number of vertices.""" @@ -55,11 +55,11 @@ class RunStatus(enum.IntEnum): class RunResult(NamedTuple): """Represent the result of running a solver on a graph.""" status: RunStatus = RunStatus.OK - weight: int|float = 0 + weight: float = 0 run_time: Sequence[float] = () -def parse_int_or_float(s: str) -> int|float: +def parse_int_or_float(s: str) -> float: """Convert a string to integer or float value.""" try: return int(s) @@ -130,11 +130,11 @@ def write_dimacs_graph(f: TextIO, graph: Graph) -> None: print(f"e {x+1} {y+1} {w:.12g}", file=f) -def read_dimacs_matching(f: TextIO) -> tuple[int|float, Matching]: +def read_dimacs_matching(f: TextIO) -> tuple[float, Matching]: """Read a matching solution in DIMACS format.""" have_weight = False - weight: int|float = 0 + weight: float = 0 pairs: list[tuple[int, int]] = [] for line in f: @@ -208,9 +208,9 @@ def make_random_graph( rng.shuffle(edge_candidates) edge_set.update(edge_candidates[:m]) - edges: list[tuple[int, int, int|float]] = [] + edges: list[tuple[int, int, float]] = [] for (x, y) in sorted(edge_set): - w: int|float + w: float if float_weights: w = rng.uniform(1.0e-8, max_weight) else: @@ -220,14 +220,14 @@ def make_random_graph( return Graph(edges) -def check_matching(graph: Graph, matching: Matching) -> int|float: +def check_matching(graph: Graph, matching: Matching) -> float: """Verify that the matching is valid and calculate its weight.""" - edge_map: dict[tuple[int, int], int|float] = {} + edge_map: dict[tuple[int, int], float] = {} for (x, y, w) in graph.edges: edge_map[(min(x, y), max(x, y))] = w - weight: int|float = 0 + weight: float = 0 nodes_used: set[int] = set() for pair in matching.pairs: @@ -247,7 +247,7 @@ def check_matching(graph: Graph, matching: Matching) -> int|float: return weight -def compare_weight(weight1: int|float, weight2: int|float) -> int: +def compare_weight(weight1: float, weight2: float) -> int: """Compare weights of matchings. Returns: @@ -406,7 +406,7 @@ class WmatchSolver(Solver): num_edge = len(graph.edges) all_integer = True - adjacent: list[list[tuple[int, int|float]]] = [ + adjacent: list[list[tuple[int, float]]] = [ [] for i in range(num_vertex)] for (x, y, w) in graph.edges: @@ -451,13 +451,13 @@ def test_solver_on_graph( solver: Solver, graph: Graph, graph_desc: str, - gold_weight: Optional[int|float], + gold_weight: Optional[float], num_run: int ) -> RunResult: """Test the specified solver with the specified graph.""" solver_run_time: list[float] = [] - solver_weight: Optional[int|float] = None + solver_weight: Optional[float] = None for i in range(num_run): @@ -507,7 +507,7 @@ def test_graph( solvers: list[Solver], graph: Graph, graph_desc: str, - gold_weight: Optional[int|float], + gold_weight: Optional[float], num_run: int ) -> list[RunResult]: """Test all solvers with the specified graph.""" @@ -525,7 +525,7 @@ def test_graph( if gold_weight is None: - best_weight: Optional[int|float] = None + best_weight: Optional[float] = None for result in results: if result.status == RunStatus.OK: if (best_weight is None) or (result.weight > best_weight): @@ -668,7 +668,7 @@ def test_input( file=sys.stderr) return 1 - gold_weight: Optional[int|float] = None + gold_weight: Optional[float] = None if verify: reffile = os.path.splitext(filename)[0] + ".out" try: @@ -740,7 +740,8 @@ def main() -> int: parser.add_argument("--timeout", action="store", type=float, - help="abort when solver runs longer than TIMEOUT seconds") + help="abort when solver runs longer than TIMEOUT" + " seconds") parser.add_argument("--runs", action="store", type=int,