Lazy delta updates of S-vertex duals
This commit is contained in:
parent
a23c38eb70
commit
de03796d99
|
@ -536,7 +536,15 @@ class _MatchingContext:
|
||||||
self.start_vertex_dual_2x = max(w for (_x, _y, w) in graph.edges)
|
self.start_vertex_dual_2x = max(w for (_x, _y, w) in graph.edges)
|
||||||
|
|
||||||
# Every vertex has a variable in the dual LPP.
|
# Every vertex has a variable in the dual LPP.
|
||||||
|
#
|
||||||
|
# For an unlabeled vertex "x",
|
||||||
# "vertex_dual_2x[x]" is 2 times the dual variable of vertex "x".
|
# "vertex_dual_2x[x]" is 2 times the dual variable of vertex "x".
|
||||||
|
#
|
||||||
|
# For an S-vertex "x",
|
||||||
|
# "vertex_dual_2x[x]" is 2 times the dual variable of vertex "x"
|
||||||
|
# plus two times the running sum of delta updates.
|
||||||
|
#
|
||||||
|
# For a T-vertex "x", ... TODO
|
||||||
self.vertex_dual_2x: list[float]
|
self.vertex_dual_2x: list[float]
|
||||||
self.vertex_dual_2x = num_vertex * [self.start_vertex_dual_2x]
|
self.vertex_dual_2x = num_vertex * [self.start_vertex_dual_2x]
|
||||||
|
|
||||||
|
@ -560,39 +568,33 @@ class _MatchingContext:
|
||||||
self.vertex_best_edge: list[int] = num_vertex * [-1]
|
self.vertex_best_edge: list[int] = num_vertex * [-1]
|
||||||
|
|
||||||
# Queue of S-vertices to be scanned.
|
# Queue of S-vertices to be scanned.
|
||||||
self.queue: collections.deque[int] = collections.deque()
|
self.scan_queue: collections.deque[int] = collections.deque()
|
||||||
|
|
||||||
def edge_slack_2x(self, e: int) -> float:
|
def edge_slack_2x(
|
||||||
"""Return 2 times the slack of the edge with index "e".
|
self,
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
bx: _Blossom,
|
||||||
|
by: _Blossom,
|
||||||
|
w: float
|
||||||
|
) -> float:
|
||||||
|
"""Calculate edge slack.
|
||||||
|
|
||||||
The result is only valid for edges that are not between vertices
|
Return 2 times the slack of an edge from vertex "x" in blossom "bx"
|
||||||
that belong to the same top-level blossom.
|
to vertex "y" in blossom "by" with weight "w".
|
||||||
|
|
||||||
|
The two vertices must be in different top-level blossoms.
|
||||||
|
|
||||||
Multiplication by 2 ensures that the return value is an integer
|
Multiplication by 2 ensures that the return value is an integer
|
||||||
if all edge weights are integers.
|
if all edge weights are integers.
|
||||||
|
|
||||||
This function is called O(n**2) times per stage.
|
|
||||||
"""
|
"""
|
||||||
(x, y, w) = self.graph.edges[e]
|
assert bx is not by
|
||||||
assert self.vertex_top_blossom[x] is not self.vertex_top_blossom[y]
|
|
||||||
return self.vertex_dual_2x[x] + self.vertex_dual_2x[y] - 2 * w
|
|
||||||
|
|
||||||
def edge_slack(self, e: int) -> float:
|
|
||||||
"""Return the slack of the edge with index "e".
|
|
||||||
|
|
||||||
This function must only be used for edges between two vertices
|
|
||||||
in different top-level S-blossoms. If all edge weights are
|
|
||||||
integers, the result is also an integer.
|
|
||||||
"""
|
|
||||||
(x, y, w) = self.graph.edges[e]
|
|
||||||
assert self.vertex_top_blossom[x] is not self.vertex_top_blossom[y]
|
|
||||||
dual_2x = self.vertex_dual_2x[x] + self.vertex_dual_2x[y]
|
dual_2x = self.vertex_dual_2x[x] + self.vertex_dual_2x[y]
|
||||||
if self.graph.integer_weights:
|
if bx.label == _LABEL_S:
|
||||||
assert dual_2x % 2 == 0
|
dual_2x -= self.delta_sum_2x
|
||||||
dual = dual_2x // 2
|
if by.label == _LABEL_S:
|
||||||
else:
|
dual_2x -= self.delta_sum_2x
|
||||||
dual = dual_2x / 2
|
return dual_2x - 2 * w
|
||||||
return dual - w
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Least-slack edge tracking:
|
# Least-slack edge tracking:
|
||||||
|
@ -635,7 +637,10 @@ class _MatchingContext:
|
||||||
if best_edge == -1:
|
if best_edge == -1:
|
||||||
self.vertex_best_edge[y] = e
|
self.vertex_best_edge[y] = e
|
||||||
else:
|
else:
|
||||||
best_slack = self.edge_slack_2x(best_edge)
|
(xx, yy, w) = self.graph.edges[best_edge]
|
||||||
|
bx = self.vertex_top_blossom[xx]
|
||||||
|
by = self.vertex_top_blossom[yy]
|
||||||
|
best_slack = self.edge_slack_2x(xx, yy, bx, by, w)
|
||||||
if slack < best_slack:
|
if slack < best_slack:
|
||||||
self.vertex_best_edge[y] = e
|
self.vertex_best_edge[y] = e
|
||||||
|
|
||||||
|
@ -657,7 +662,10 @@ class _MatchingContext:
|
||||||
if self.vertex_top_blossom[x].label == _LABEL_NONE:
|
if self.vertex_top_blossom[x].label == _LABEL_NONE:
|
||||||
e = self.vertex_best_edge[x]
|
e = self.vertex_best_edge[x]
|
||||||
if e != -1:
|
if e != -1:
|
||||||
slack = self.edge_slack_2x(e)
|
(x, y, w) = self.graph.edges[e]
|
||||||
|
bx = self.vertex_top_blossom[x]
|
||||||
|
by = self.vertex_top_blossom[y]
|
||||||
|
slack = self.edge_slack_2x(x, y, bx, by, w)
|
||||||
if (best_index == -1) or (slack < best_slack):
|
if (best_index == -1) or (slack < best_slack):
|
||||||
best_index = e
|
best_index = e
|
||||||
best_slack = slack
|
best_slack = slack
|
||||||
|
@ -668,6 +676,70 @@ class _MatchingContext:
|
||||||
# General support routines:
|
# General support routines:
|
||||||
#
|
#
|
||||||
|
|
||||||
|
def assign_blossom_label_s(self, blossom: _Blossom) -> None:
|
||||||
|
"""Assign label S to an unlabeled top-level blossom.
|
||||||
|
|
||||||
|
All vertices in the newly labeled blossom are added to the scan queue.
|
||||||
|
Dual variables are adjusted.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_NONE
|
||||||
|
|
||||||
|
# Assign label S.
|
||||||
|
blossom.label = _LABEL_S
|
||||||
|
|
||||||
|
# Adjust the vertex dual variables of the new S-vertices.
|
||||||
|
vertices = blossom.vertices()
|
||||||
|
for x in vertices:
|
||||||
|
self.vertex_dual_2x[x] += self.delta_sum_2x
|
||||||
|
|
||||||
|
# Add all vertices inside the newly labeled S-blossom to the queue.
|
||||||
|
self.scan_queue.extend(vertices)
|
||||||
|
|
||||||
|
def remove_blossom_label_s(self, blossom: _Blossom) -> None:
|
||||||
|
"""Remove label S from a top-level S-blossom."""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_S
|
||||||
|
|
||||||
|
# Remove label.
|
||||||
|
blossom.label = _LABEL_NONE
|
||||||
|
|
||||||
|
# Adjust the vertex dual variables of the former S-vertices.
|
||||||
|
for x in blossom.vertices():
|
||||||
|
self.vertex_dual_2x[x] -= self.delta_sum_2x
|
||||||
|
|
||||||
|
def assign_blossom_label_t(self, blossom: _Blossom) -> None:
|
||||||
|
"""Assign label T to an unlabeled top-level blossom."""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_NONE
|
||||||
|
|
||||||
|
# Assign label T.
|
||||||
|
blossom.label = _LABEL_T
|
||||||
|
|
||||||
|
# Insert blossom into the delta4 queue.
|
||||||
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
|
assert blossom.delta4_node is None
|
||||||
|
prio = blossom.dual_var + self.delta_sum_2x
|
||||||
|
blossom.delta4_node = self.delta4_queue.insert(prio, blossom)
|
||||||
|
|
||||||
|
def remove_blossom_label_t(self, blossom: _Blossom) -> None:
|
||||||
|
"""Remove label T from a top-level T-blossom."""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_T
|
||||||
|
|
||||||
|
# Remove label.
|
||||||
|
blossom.label = _LABEL_NONE
|
||||||
|
|
||||||
|
# Remove blossom from delta4 queue.
|
||||||
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
|
assert blossom.delta4_node is not None
|
||||||
|
self.delta4_queue.delete(blossom.delta4_node)
|
||||||
|
blossom.delta4_node = None
|
||||||
|
|
||||||
def reset_stage(self) -> None:
|
def reset_stage(self) -> None:
|
||||||
"""Reset data which are only valid during a stage.
|
"""Reset data which are only valid during a stage.
|
||||||
|
|
||||||
|
@ -679,11 +751,16 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Remove blossom labels.
|
# Remove blossom labels.
|
||||||
for blossom in self.trivial_blossom + self.nontrivial_blossom:
|
for blossom in self.trivial_blossom + self.nontrivial_blossom:
|
||||||
|
if blossom.parent is None:
|
||||||
|
if blossom.label == _LABEL_S:
|
||||||
|
self.remove_blossom_label_s(blossom)
|
||||||
|
elif blossom.label == _LABEL_T:
|
||||||
|
self.remove_blossom_label_t(blossom)
|
||||||
blossom.label = _LABEL_NONE
|
blossom.label = _LABEL_NONE
|
||||||
blossom.tree_edge = None
|
blossom.tree_edge = None
|
||||||
|
|
||||||
# Clear the queue.
|
# Clear the scan queue.
|
||||||
self.queue.clear()
|
self.scan_queue.clear()
|
||||||
|
|
||||||
# Reset least-slack edge tracking.
|
# Reset least-slack edge tracking.
|
||||||
self.lset_reset()
|
self.lset_reset()
|
||||||
|
@ -691,10 +768,7 @@ class _MatchingContext:
|
||||||
# Reset delta queues.
|
# Reset delta queues.
|
||||||
self.delta3_queue.clear()
|
self.delta3_queue.clear()
|
||||||
self.delta3_set.clear()
|
self.delta3_set.clear()
|
||||||
self.delta4_queue.clear()
|
assert self.delta4_queue.empty()
|
||||||
|
|
||||||
for ntb in self.nontrivial_blossom:
|
|
||||||
ntb.delta4_node = None
|
|
||||||
|
|
||||||
def trace_alternating_paths(self, x: int, y: int) -> _AlternatingPath:
|
def trace_alternating_paths(self, x: int, y: int) -> _AlternatingPath:
|
||||||
"""Trace back through the alternating trees from vertices "x" and "y".
|
"""Trace back through the alternating trees from vertices "x" and "y".
|
||||||
|
@ -797,16 +871,30 @@ class _MatchingContext:
|
||||||
subblossoms = [self.vertex_top_blossom[x] for (x, y) in path.edges]
|
subblossoms = [self.vertex_top_blossom[x] for (x, y) in path.edges]
|
||||||
|
|
||||||
# Check that the path is cyclic.
|
# Check that the path is cyclic.
|
||||||
# Note the path may not start and end with the same _vertex_,
|
# Note the path will not always start and end with the same _vertex_,
|
||||||
# but it must start and end in the same _blossom_.
|
# but it must start and end in the same _blossom_.
|
||||||
subblossoms_next = [self.vertex_top_blossom[y]
|
subblossoms_next = [self.vertex_top_blossom[y]
|
||||||
for (x, y) in path.edges]
|
for (x, y) in path.edges]
|
||||||
assert subblossoms[0] == subblossoms_next[-1]
|
assert subblossoms[0] == subblossoms_next[-1]
|
||||||
assert subblossoms[1:] == subblossoms_next[:-1]
|
assert subblossoms[1:] == subblossoms_next[:-1]
|
||||||
|
|
||||||
|
# Blossom must start and end with an S-sub-blossom.
|
||||||
|
assert subblossoms[0].label == _LABEL_S
|
||||||
|
|
||||||
|
# Relabel the T-sub-blossoms to S-sub-blossoms.
|
||||||
|
# This also adds new S-vertices to the scan queue.
|
||||||
|
for sub in subblossoms:
|
||||||
|
if sub.label == _LABEL_T:
|
||||||
|
self.remove_blossom_label_t(sub)
|
||||||
|
self.assign_blossom_label_s(sub)
|
||||||
|
|
||||||
# Create the new blossom object.
|
# Create the new blossom object.
|
||||||
blossom = _NonTrivialBlossom(subblossoms, path.edges)
|
blossom = _NonTrivialBlossom(subblossoms, path.edges)
|
||||||
|
|
||||||
|
# Assign label S to the new blossom and link it to the tree.
|
||||||
|
blossom.label = _LABEL_S
|
||||||
|
blossom.tree_edge = subblossoms[0].tree_edge
|
||||||
|
|
||||||
# Insert into the blossom array.
|
# Insert into the blossom array.
|
||||||
self.nontrivial_blossom.append(blossom)
|
self.nontrivial_blossom.append(blossom)
|
||||||
|
|
||||||
|
@ -818,24 +906,6 @@ class _MatchingContext:
|
||||||
for x in blossom.vertices():
|
for x in blossom.vertices():
|
||||||
self.vertex_top_blossom[x] = blossom
|
self.vertex_top_blossom[x] = blossom
|
||||||
|
|
||||||
# Assign label S to the new blossom.
|
|
||||||
assert subblossoms[0].label == _LABEL_S
|
|
||||||
blossom.label = _LABEL_S
|
|
||||||
blossom.tree_edge = subblossoms[0].tree_edge
|
|
||||||
|
|
||||||
# Former T-vertices which are part of this blossom now become
|
|
||||||
# S-vertices. Add them to the queue.
|
|
||||||
for sub in subblossoms:
|
|
||||||
if sub.label == _LABEL_T:
|
|
||||||
self.queue.extend(sub.vertices())
|
|
||||||
|
|
||||||
# Remove sub-blossoms from the delta4 queue.
|
|
||||||
for sub in subblossoms:
|
|
||||||
if ((isinstance(sub, _NonTrivialBlossom))
|
|
||||||
and (sub.delta4_node is not None)):
|
|
||||||
self.delta4_queue.delete(sub.delta4_node)
|
|
||||||
sub.delta4_node = None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_path_through_blossom(
|
def find_path_through_blossom(
|
||||||
blossom: _NonTrivialBlossom,
|
blossom: _NonTrivialBlossom,
|
||||||
|
@ -875,6 +945,7 @@ class _MatchingContext:
|
||||||
# Remove expanded blossom from the delta4 queue.
|
# Remove expanded blossom from the delta4 queue.
|
||||||
assert blossom.delta4_node is not None
|
assert blossom.delta4_node is not None
|
||||||
self.delta4_queue.delete(blossom.delta4_node)
|
self.delta4_queue.delete(blossom.delta4_node)
|
||||||
|
blossom.delta4_node = None
|
||||||
|
|
||||||
# Convert sub-blossoms into top-level blossoms.
|
# Convert sub-blossoms into top-level blossoms.
|
||||||
for sub in blossom.subblossoms:
|
for sub in blossom.subblossoms:
|
||||||
|
@ -899,15 +970,9 @@ class _MatchingContext:
|
||||||
sub = self.vertex_top_blossom[y]
|
sub = self.vertex_top_blossom[y]
|
||||||
|
|
||||||
# Assign label T to that sub-blossom.
|
# Assign label T to that sub-blossom.
|
||||||
sub.label = _LABEL_T
|
self.assign_blossom_label_t(sub)
|
||||||
sub.tree_edge = blossom.tree_edge
|
sub.tree_edge = blossom.tree_edge
|
||||||
|
|
||||||
# Insert T sub-blossom into the delta4 queue.
|
|
||||||
if isinstance(sub, _NonTrivialBlossom):
|
|
||||||
assert sub.delta4_node is None
|
|
||||||
prio = sub.dual_var + self.delta_sum_2x
|
|
||||||
sub.delta4_node = self.delta4_queue.insert(prio, sub)
|
|
||||||
|
|
||||||
# Walk through the expanded blossom from "sub" to the base vertex.
|
# Walk through the expanded blossom from "sub" to the base vertex.
|
||||||
# Assign alternating S and T labels to the sub-blossoms and attach
|
# Assign alternating S and T labels to the sub-blossoms and attach
|
||||||
# them to the alternating tree.
|
# them to the alternating tree.
|
||||||
|
@ -924,20 +989,14 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Assign label S to path_nodes[p+1].
|
# Assign label S to path_nodes[p+1].
|
||||||
(y, x) = path_edges[p]
|
(y, x) = path_edges[p]
|
||||||
self.assign_label_s(x)
|
self.extend_tree_s(x)
|
||||||
|
|
||||||
# Assign label T to path_nodes[i+2] and attach it
|
# Assign label T to path_nodes[i+2] and attach it
|
||||||
# to path_nodes[p+1].
|
# to path_nodes[p+1].
|
||||||
sub = path_nodes[p+2]
|
sub = path_nodes[p+2]
|
||||||
sub.label = _LABEL_T
|
self.assign_blossom_label_t(sub)
|
||||||
sub.tree_edge = path_edges[p+1]
|
sub.tree_edge = path_edges[p+1]
|
||||||
|
|
||||||
# Insert T sub-blossom into the delta4 queue.
|
|
||||||
if isinstance(sub, _NonTrivialBlossom):
|
|
||||||
assert sub.delta4_node is None
|
|
||||||
prio = sub.dual_var + self.delta_sum_2x
|
|
||||||
sub.delta4_node = self.delta4_queue.insert(prio, sub)
|
|
||||||
|
|
||||||
# Delete the expanded blossom.
|
# Delete the expanded blossom.
|
||||||
self.nontrivial_blossom.remove(blossom)
|
self.nontrivial_blossom.remove(blossom)
|
||||||
|
|
||||||
|
@ -1109,7 +1168,7 @@ class _MatchingContext:
|
||||||
# Labeling and alternating tree expansion:
|
# Labeling and alternating tree expansion:
|
||||||
#
|
#
|
||||||
|
|
||||||
def assign_label_s(self, x: int) -> None:
|
def extend_tree_s(self, x: int) -> None:
|
||||||
"""Assign label S to the unlabeled blossom that contains vertex "x".
|
"""Assign label S to the unlabeled blossom that contains vertex "x".
|
||||||
|
|
||||||
If vertex "x" is matched, it is attached to the alternating tree
|
If vertex "x" is matched, it is attached to the alternating tree
|
||||||
|
@ -1125,8 +1184,7 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Assign label S to the blossom that contains vertex "x".
|
# Assign label S to the blossom that contains vertex "x".
|
||||||
bx = self.vertex_top_blossom[x]
|
bx = self.vertex_top_blossom[x]
|
||||||
assert bx.label == _LABEL_NONE
|
self.assign_blossom_label_s(bx)
|
||||||
bx.label = _LABEL_S
|
|
||||||
|
|
||||||
y = self.vertex_mate[x]
|
y = self.vertex_mate[x]
|
||||||
if y == -1:
|
if y == -1:
|
||||||
|
@ -1146,10 +1204,7 @@ class _MatchingContext:
|
||||||
# Attach the blossom that contains "x" to the alternating tree.
|
# Attach the blossom that contains "x" to the alternating tree.
|
||||||
bx.tree_edge = (y, x)
|
bx.tree_edge = (y, x)
|
||||||
|
|
||||||
# Add all vertices inside the newly labeled S-blossom to the queue.
|
def extend_tree_t(self, x: int, y: int) -> None:
|
||||||
self.queue.extend(bx.vertices())
|
|
||||||
|
|
||||||
def assign_label_t(self, x: int, y: int) -> None:
|
|
||||||
"""Assign label T to the unlabeled blossom that contains vertex "y".
|
"""Assign label T to the unlabeled blossom that contains vertex "y".
|
||||||
|
|
||||||
Attach it to the alternating tree via edge (x, y).
|
Attach it to the alternating tree via edge (x, y).
|
||||||
|
@ -1170,20 +1225,13 @@ class _MatchingContext:
|
||||||
by = self.vertex_top_blossom[y]
|
by = self.vertex_top_blossom[y]
|
||||||
|
|
||||||
# Assign label T to the unlabeled blossom.
|
# Assign label T to the unlabeled blossom.
|
||||||
assert by.label == _LABEL_NONE
|
self.assign_blossom_label_t(by)
|
||||||
by.label = _LABEL_T
|
|
||||||
by.tree_edge = (x, y)
|
by.tree_edge = (x, y)
|
||||||
|
|
||||||
# Insert T sub-blossom into the delta4 queue.
|
|
||||||
if isinstance(by, _NonTrivialBlossom):
|
|
||||||
assert by.delta4_node is None
|
|
||||||
prio = by.dual_var + self.delta_sum_2x
|
|
||||||
by.delta4_node = self.delta4_queue.insert(prio, by)
|
|
||||||
|
|
||||||
# Assign label S to the blossom that is mated to the T-blossom.
|
# Assign label S to the blossom that is mated to the T-blossom.
|
||||||
z = self.vertex_mate[by.base_vertex]
|
z = self.vertex_mate[by.base_vertex]
|
||||||
assert z != -1
|
assert z != -1
|
||||||
self.assign_label_s(z)
|
self.extend_tree_s(z)
|
||||||
|
|
||||||
def add_s_to_s_edge(self, x: int, y: int) -> Optional[_AlternatingPath]:
|
def add_s_to_s_edge(self, x: int, y: int) -> Optional[_AlternatingPath]:
|
||||||
"""Add the edge between S-vertices "x" and "y".
|
"""Add the edge between S-vertices "x" and "y".
|
||||||
|
@ -1231,10 +1279,10 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Process S-vertices waiting to be scanned.
|
# Process S-vertices waiting to be scanned.
|
||||||
# This loop runs through O(n) iterations per stage.
|
# This loop runs through O(n) iterations per stage.
|
||||||
while self.queue:
|
while self.scan_queue:
|
||||||
|
|
||||||
# Take a vertex from the queue.
|
# Take a vertex from the queue.
|
||||||
x = self.queue.popleft()
|
x = self.scan_queue.popleft()
|
||||||
|
|
||||||
# Double-check that "x" is an S-vertex.
|
# Double-check that "x" is an S-vertex.
|
||||||
bx = self.vertex_top_blossom[x]
|
bx = self.vertex_top_blossom[x]
|
||||||
|
@ -1243,7 +1291,7 @@ class _MatchingContext:
|
||||||
# Scan the edges that are incident on "x".
|
# Scan the edges that are incident on "x".
|
||||||
# This loop runs through O(m) iterations per stage.
|
# This loop runs through O(m) iterations per stage.
|
||||||
for e in adjacent_edges[x]:
|
for e in adjacent_edges[x]:
|
||||||
(p, q, _w) = edges[e]
|
(p, q, w) = edges[e]
|
||||||
y = p if p != x else q
|
y = p if p != x else q
|
||||||
|
|
||||||
# Consider the edge between vertices "x" and "y".
|
# Consider the edge between vertices "x" and "y".
|
||||||
|
@ -1262,11 +1310,11 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Check whether this edge is tight (has zero slack).
|
# Check whether this edge is tight (has zero slack).
|
||||||
# Only tight edges may be part of an alternating tree.
|
# Only tight edges may be part of an alternating tree.
|
||||||
slack = self.edge_slack_2x(e)
|
slack = self.edge_slack_2x(x, y, bx, by, w)
|
||||||
if slack <= 0:
|
if slack <= 0:
|
||||||
if ylabel == _LABEL_NONE:
|
if ylabel == _LABEL_NONE:
|
||||||
# Assign label T to the blossom that contains "y".
|
# Assign label T to the blossom that contains "y".
|
||||||
self.assign_label_t(x, y)
|
self.extend_tree_t(x, y)
|
||||||
elif ylabel == _LABEL_S:
|
elif ylabel == _LABEL_S:
|
||||||
# This edge connects two S-blossoms. Use it to find
|
# This edge connects two S-blossoms. Use it to find
|
||||||
# either a new blossom or an augmenting path.
|
# either a new blossom or an augmenting path.
|
||||||
|
@ -1276,8 +1324,17 @@ class _MatchingContext:
|
||||||
|
|
||||||
elif ylabel == _LABEL_S:
|
elif ylabel == _LABEL_S:
|
||||||
# Update tracking of least-slack edges between S-blossoms.
|
# Update tracking of least-slack edges between S-blossoms.
|
||||||
|
# Priority is edge slack plus 2 times the running sum of
|
||||||
|
# delta updates.
|
||||||
if e not in self.delta3_set:
|
if e not in self.delta3_set:
|
||||||
prio = self.edge_slack(e) + self.delta_sum_2x
|
if self.graph.integer_weights:
|
||||||
|
# If all edge weights are integers, the slack of
|
||||||
|
# any edge between S-vertices is also an integer.
|
||||||
|
assert slack % 2 == 0
|
||||||
|
slack_1x = slack // 2
|
||||||
|
else:
|
||||||
|
slack_1x = slack / 2
|
||||||
|
prio = slack_1x + self.delta_sum_2x
|
||||||
self.delta3_set.add(e)
|
self.delta3_set.add(e)
|
||||||
self.delta3_queue.insert(prio, e)
|
self.delta3_queue.insert(prio, e)
|
||||||
|
|
||||||
|
@ -1342,7 +1399,7 @@ class _MatchingContext:
|
||||||
assert (bx.label == _LABEL_S) and (by.label == _LABEL_S)
|
assert (bx.label == _LABEL_S) and (by.label == _LABEL_S)
|
||||||
if bx is not by:
|
if bx is not by:
|
||||||
# Found edge between different top-level S-blossoms.
|
# Found edge between different top-level S-blossoms.
|
||||||
slack = self.edge_slack(e)
|
slack = delta3_node.prio - self.delta_sum_2x
|
||||||
if slack <= delta_2x:
|
if slack <= delta_2x:
|
||||||
delta_type = 3
|
delta_type = 3
|
||||||
delta_2x = slack
|
delta_2x = slack
|
||||||
|
@ -1378,10 +1435,7 @@ class _MatchingContext:
|
||||||
# Apply delta to dual variables of all vertices.
|
# Apply delta to dual variables of all vertices.
|
||||||
for x in range(num_vertex):
|
for x in range(num_vertex):
|
||||||
xlabel = self.vertex_top_blossom[x].label
|
xlabel = self.vertex_top_blossom[x].label
|
||||||
if xlabel == _LABEL_S:
|
if xlabel == _LABEL_T:
|
||||||
# S-vertex: subtract delta from dual variable.
|
|
||||||
self.vertex_dual_2x[x] -= delta_2x
|
|
||||||
elif xlabel == _LABEL_T:
|
|
||||||
# T-vertex: add delta to dual variable.
|
# T-vertex: add delta to dual variable.
|
||||||
self.vertex_dual_2x[x] += delta_2x
|
self.vertex_dual_2x[x] += delta_2x
|
||||||
|
|
||||||
|
@ -1420,12 +1474,12 @@ class _MatchingContext:
|
||||||
# Assign label S to all unmatched vertices and put them in the queue.
|
# Assign label S to all unmatched vertices and put them in the queue.
|
||||||
for x in range(num_vertex):
|
for x in range(num_vertex):
|
||||||
if self.vertex_mate[x] == -1:
|
if self.vertex_mate[x] == -1:
|
||||||
self.assign_label_s(x)
|
self.extend_tree_s(x)
|
||||||
|
|
||||||
# Stop if all vertices are matched.
|
# Stop if all vertices are matched.
|
||||||
# No further improvement is possible in that case.
|
# No further improvement is possible in that case.
|
||||||
# This avoids messy calculations of delta steps without any S-vertex.
|
# This avoids messy calculations of delta steps without any S-vertex.
|
||||||
if not self.queue:
|
if not self.scan_queue:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Each pass through the following loop is a "substage".
|
# Each pass through the following loop is a "substage".
|
||||||
|
@ -1457,7 +1511,7 @@ class _MatchingContext:
|
||||||
(x, y, _w) = self.graph.edges[delta_edge]
|
(x, y, _w) = self.graph.edges[delta_edge]
|
||||||
if self.vertex_top_blossom[x].label != _LABEL_S:
|
if self.vertex_top_blossom[x].label != _LABEL_S:
|
||||||
(x, y) = (y, x)
|
(x, y) = (y, x)
|
||||||
self.assign_label_t(x, y)
|
self.extend_tree_t(x, y)
|
||||||
|
|
||||||
elif delta_type == 3:
|
elif delta_type == 3:
|
||||||
# Use the S-to-S edge that got unlocked by the delta update.
|
# Use the S-to-S edge that got unlocked by the delta update.
|
||||||
|
|
Loading…
Reference in New Issue