1
0
Fork 0

Minor cleanup in scanning and delta steps

This commit is contained in:
Joris van Rantwijk 2024-06-30 16:13:40 +02:00
parent f35a640e43
commit 0e76e6472b
2 changed files with 33 additions and 27 deletions

View File

@ -1078,7 +1078,7 @@ class _MatchingContext:
Marks the blossoms as unlabeled.
Updates delta tracking accordingly.
This function takes time O((n+m) * log(n)).
This function takes time O((n + m) * log(n)).
"""
for blossom in tree_blossoms:
assert blossom.label != _LABEL_NONE
@ -1098,9 +1098,9 @@ class _MatchingContext:
discovers an augmenting path. In this case it returns an alternating
path that starts and ends in an unmatched vertex.
This function takes time O(k*log(n)) to discover a blossom,
This function takes time O(k * log(n)) to discover a blossom,
where "k" is the number of sub-blossoms,
or time O(n*log(n)) to discover an augmenting path.
or time O(n * log(n)) to discover an augmenting path.
Returns:
Alternating path as an ordered list of edges between top-level
@ -1180,7 +1180,7 @@ class _MatchingContext:
Assign label S to the new blossom.
Relabel all T-sub-blossoms as S and add their vertices to the queue.
This function takes total time O(n*log(n)) per stage.
This function takes total time O(n * log(n)) per stage.
"""
# Check that the path is odd-length.
@ -1635,14 +1635,22 @@ class _MatchingContext:
return True
def substage_scan(self) -> None:
"""Scan queued S-vertices and consider their incident edges.
def scan_new_s_vertices(self) -> None:
"""Scan the incident edges of newly labeled S-vertices.
Edges are added to delta2 tracking or delta3 tracking depending
on the state of the vertex on the opposite side of the edge.
Edges are inserted in delta2 and delta3 tracking.
This function does not yet use the edges to extend the alternating
tree or find blossoms or augmenting paths, even if the edges
are tight. An edge that is already tight may be used later through
a zero-delta step.
are tight. If there are such tight edges, they will be used later
through zero-delta steps.
If there are "j" new S-vertices with a total of "k" incident edges,
this function takes time O((j + k) * log(n)).
Since each vertex can become an S-vertex at most once per stage,
this function takes total time O((n + m) * log(n)) per stage.
"""
edges = self.graph.edges
@ -1688,7 +1696,7 @@ class _MatchingContext:
# Delta steps:
#
def substage_calc_dual_delta(
def calc_dual_delta_step(
self
) -> tuple[int, float, int, Optional[_NonTrivialBlossom]]:
"""Calculate a delta step in the dual LPP problem.
@ -1701,9 +1709,13 @@ class _MatchingContext:
Multiplication by 2 ensures that the result is an integer if all edge
weights are integers.
This function assumes that there is at least one S-vertex.
This function takes total time O(m * log(n)) for all calls
within a stage.
This function takes time O((1 + k) * log(n)),
where "k" is the number of intra-blossom edges removed from
the delta3 queue.
Since each edge can be inserted into the delta3 queue at most
once per stage, this function takes total time O((n + m) * log(n))
per stage.
Returns:
Tuple (delta_type, delta_2x, delta_edge, delta_blossom).
@ -1788,12 +1800,6 @@ class _MatchingContext:
False if no further improvement is possible.
"""
# Stop if all vertices are matched.
# No further improvement is possible in that case.
# This avoids messy calculations of delta steps without any S-vertex.
if all(y >= 0 for y in self.vertex_mate):
return False
# Each pass through the following loop is a "substage".
# The substage tries to find an augmenting path.
# If an augmenting path is found, we augment the matching and end
@ -1806,11 +1812,11 @@ class _MatchingContext:
self._check_alternating_tree_consistency() # TODO -- remove this
# Consider the incident edges of newly labeled S-vertices.
self.substage_scan()
self.scan_new_s_vertices()
# Calculate delta step in the dual LPP problem.
(delta_type, delta_2x, delta_edge, delta_blossom
) = self.substage_calc_dual_delta()
) = self.calc_dual_delta_step()
# Update the running sum of delta steps.
# This implicitly updates the dual variables as needed, because

View File

@ -24,22 +24,22 @@ def patch_matching_code() -> None:
import mwmatching
orig_make_blossom = mwmatching._MatchingContext.make_blossom
orig_substage_calc_dual_delta = (
mwmatching._MatchingContext.substage_calc_dual_delta)
orig_calc_dual_delta_step = (
mwmatching._MatchingContext.calc_dual_delta_step)
def stub_make_blossom(*args: Any, **kwargs: Any) -> Any:
count_make_blossom[0] += 1
return orig_make_blossom(*args, **kwargs)
def stub_substage_calc_dual_delta(*args: Any, **kwargs: Any) -> Any:
def stub_calc_dual_delta_step(*args: Any, **kwargs: Any) -> Any:
count_delta_step[0] += 1
ret = orig_substage_calc_dual_delta(*args, **kwargs)
ret = orig_calc_dual_delta_step(*args, **kwargs)
return ret
mwmatching._MatchingContext.make_blossom = ( # type: ignore
stub_make_blossom)
mwmatching._MatchingContext.substage_calc_dual_delta = ( # type: ignore
stub_substage_calc_dual_delta)
mwmatching._MatchingContext.calc_dual_delta_step = ( # type: ignore
stub_calc_dual_delta_step)
def run_max_weight_matching(