diff --git a/python/mwmatching.py b/python/mwmatching.py index a2ed016..f05f453 100644 --- a/python/mwmatching.py +++ b/python/mwmatching.py @@ -12,7 +12,7 @@ from typing import NamedTuple, Optional def maximum_weight_matching( - edges: Sequence[tuple[int, int, int|float]] + edges: Sequence[tuple[int, int, float]] ) -> list[tuple[int, int]]: """Compute a maximum-weighted matching in the general undirected weighted graph given by "edges". @@ -93,8 +93,8 @@ def maximum_weight_matching( def adjust_weights_for_maximum_cardinality_matching( - edges: Sequence[tuple[int, int, int|float]] - ) -> Sequence[tuple[int, int, int|float]]: + edges: Sequence[tuple[int, int, float]] + ) -> Sequence[tuple[int, int, float]]: """Adjust edge weights such that the maximum-weight matching of the adjusted graph is a maximum-cardinality matching, equal to a matching in the original graph that has maximum weight out of all @@ -157,7 +157,7 @@ def adjust_weights_for_maximum_cardinality_matching( if min_weight > 0 and min_weight >= num_vertex * weight_range: return edges - delta: int|float + delta: float if weight_range > 0: # Increase weights to make minimum edge weight large enough # to improve any non-maximum-cardinality matching. @@ -180,7 +180,7 @@ class MatchingError(Exception): pass -def _check_input_types(edges: Sequence[tuple[int, int, int|float]]) -> None: +def _check_input_types(edges: Sequence[tuple[int, int, float]]) -> None: """Check that the input consists of valid data types and valid numerical ranges. @@ -227,7 +227,7 @@ def _check_input_types(edges: Sequence[tuple[int, int, int|float]]) -> None: f" less than {float_limit:g}") -def _check_input_graph(edges: Sequence[tuple[int, int, int|float]]) -> None: +def _check_input_graph(edges: Sequence[tuple[int, int, float]]) -> None: """Check that the input is a valid graph, without any multi-edges and without any self-edges. @@ -259,8 +259,8 @@ def _check_input_graph(edges: Sequence[tuple[int, int, int|float]]) -> None: def _remove_negative_weight_edges( - edges: Sequence[tuple[int, int, int|float]] - ) -> Sequence[tuple[int, int, int|float]]: + edges: Sequence[tuple[int, int, float]] + ) -> Sequence[tuple[int, int, float]]: """Remove edges with negative weight. This does not change the solution of the maximum-weight matching problem, @@ -278,7 +278,7 @@ class _GraphInfo: These data remain unchanged while the algorithm runs. """ - def __init__(self, edges: Sequence[tuple[int, int, int|float]]) -> None: + def __init__(self, edges: Sequence[tuple[int, int, float]]) -> None: """Initialize the graph representation and prepare an adjacency list. This function takes time O(n + m). @@ -296,7 +296,7 @@ class _GraphInfo: # "w" is the edge weight. # # These data remain unchanged while the algorithm runs. - self.edges: Sequence[tuple[int, int, int|float]] = edges + self.edges: Sequence[tuple[int, int, float]] = edges # num_vertex = the number of vertices. if edges: @@ -448,7 +448,7 @@ class _NonTrivialBlossom(_Blossom): # # "dual_var" is the current value of the dual variable. # New blossoms start with dual variable 0. - self.dual_var: int|float = 0 + self.dual_var: float = 0 # For a non-trivial, top-level S-blossom, # "best_edge_set" is a list of least-slack edges between this blossom @@ -538,7 +538,7 @@ class _MatchingContext: # # Vertex duals are initialized to half the maximum edge weight. max_weight = max(w for (_x, _y, w) in graph.edges) - self.vertex_dual_2x: list[int|float] = num_vertex * [max_weight] + self.vertex_dual_2x: list[float] = num_vertex * [max_weight] # For each T-vertex or unlabeled vertex "x", # "vertex_best_edge[x]" is the edge index of the least-slack edge @@ -548,7 +548,7 @@ class _MatchingContext: # Queue of S-vertices to be scanned. self.queue: collections.deque[int] = collections.deque() - def edge_slack_2x(self, e: int) -> int|float: + def edge_slack_2x(self, e: int) -> float: """Return 2 times the slack of the edge with index "e". The result is only valid for edges that are not between vertices @@ -611,7 +611,7 @@ class _MatchingContext: blossom.best_edge = -1 blossom.best_edge_set = None - def lset_add_vertex_edge(self, y: int, e: int, slack: int|float) -> None: + def lset_add_vertex_edge(self, y: int, e: int, slack: float) -> None: """Add edge "e" from an S-vertex to unlabeled vertex or T-vertex "y". This function takes time O(1) per call. @@ -625,7 +625,7 @@ class _MatchingContext: if slack < best_slack: self.vertex_best_edge[y] = e - def lset_get_best_vertex_edge(self) -> tuple[int, int|float]: + def lset_get_best_vertex_edge(self) -> tuple[int, float]: """Return the index and slack of the least-slack edge between any S-vertex and unlabeled vertex. @@ -637,7 +637,7 @@ class _MatchingContext: or (-1, 0) if there is no suitable edge. """ best_index = -1 - best_slack: int|float = 0 + best_slack: float = 0 for x in range(self.graph.num_vertex): if self.vertex_top_blossom[x].label == _LABEL_NONE: @@ -662,7 +662,7 @@ class _MatchingContext: self, blossom: _Blossom, e: int, - slack: int|float + slack: float ) -> None: """Add edge "e" between the specified S-blossom and another S-blossom. @@ -708,12 +708,12 @@ class _MatchingContext: # each top-level S-blossom. This array is indexed by the base vertex # of the blossoms. best_edge_to_blossom: list[int] = num_vertex * [-1] - zero_slack: int|float = 0 - best_slack_to_blossom: list[int|float] = num_vertex * [zero_slack] + zero_slack: float = 0 + best_slack_to_blossom: list[float] = num_vertex * [zero_slack] # And find the overall least-slack edge to any other S-blossom. best_edge = -1 - best_slack: int|float = 0 + best_slack: float = 0 # Add the least-slack edges of every S-sub-blossom. for sub in blossom.subblossoms: @@ -774,7 +774,7 @@ class _MatchingContext: # Keep the overall least-slack edge. blossom.best_edge = best_edge - def lset_get_best_blossom_edge(self) -> tuple[int, int|float]: + def lset_get_best_blossom_edge(self) -> tuple[int, float]: """Return the index and slack of the least-slack edge between any pair of top-level S-blossoms. @@ -786,7 +786,7 @@ class _MatchingContext: or (-1, 0) if there is no suitable edge. """ best_index = -1 - best_slack: int|float = 0 + best_slack: float = 0 for blossom in self.trivial_blossom + self.nontrivial_blossom: if (blossom.label == _LABEL_S) and (blossom.parent is None): @@ -1396,7 +1396,7 @@ class _MatchingContext: def substage_calc_dual_delta( self - ) -> tuple[int, float|int, int, Optional[_NonTrivialBlossom]]: + ) -> tuple[int, float, int, Optional[_NonTrivialBlossom]]: """Calculate a delta step in the dual LPP problem. This function returns the minimum of the 4 types of delta values, @@ -1460,7 +1460,7 @@ class _MatchingContext: return (delta_type, delta_2x, delta_edge, delta_blossom) - def substage_apply_delta_step(self, delta_2x: int|float) -> None: + def substage_apply_delta_step(self, delta_2x: float) -> None: """Apply a delta step to the dual LPP variables.""" num_vertex = self.graph.num_vertex @@ -1582,7 +1582,7 @@ class _MatchingContext: def _verify_blossom_edges( ctx: _MatchingContext, blossom: _NonTrivialBlossom, - edge_slack_2x: list[int|float] + edge_slack_2x: list[float] ) -> None: """Descend down the blossom tree to find edges that are contained in blossoms. @@ -1610,7 +1610,7 @@ def _verify_blossom_edges( # Keep track of the sum of blossom duals at each depth along # the current descent path. - path_sum_dual: list[int|float] = [0] + path_sum_dual: list[float] = [0] # Keep track of the number of matched edges at each depth along # the current descent path. @@ -1759,7 +1759,7 @@ def _verify_optimum(ctx: _MatchingContext) -> None: # Calculate the slack of each edge. # A correction will be needed for edges inside blossoms. - edge_slack_2x: list[int|float] = [ + edge_slack_2x: list[float] = [ ctx.vertex_dual_2x[x] + ctx.vertex_dual_2x[y] - 2 * w for (x, y, w) in ctx.graph.edges]