1
0
Fork 0

Lazy delta updates of S-blossom duals

This commit is contained in:
Joris van Rantwijk 2024-05-26 00:07:16 +02:00
parent de03796d99
commit b2e055b357
1 changed files with 43 additions and 36 deletions

View File

@ -444,7 +444,13 @@ class _NonTrivialBlossom(_Blossom):
# Every non-trivial blossom has a variable in the dual LPP.
#
# If this is a top-level S-blossom,
# "dual_var" is the value of the dual variable minus 2 times
# the running sum of delta steps.
#
# In all other cases,
# "dual_var" is the current value of the dual variable.
#
# New blossoms start with dual variable 0.
self.dual_var: float = 0
@ -542,7 +548,7 @@ class _MatchingContext:
#
# 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.
# plus two times the running sum of delta steps.
#
# For a T-vertex "x", ... TODO
self.vertex_dual_2x: list[float]
@ -553,13 +559,13 @@ class _MatchingContext:
# Queue containing edges between S-vertices in different top-level
# blossoms. The priority of an edge is its slack plus two times the
# running sum of delta updates.
# running sum of delta steps.
self.delta3_queue: PriorityQueue[int] = PriorityQueue()
self.delta3_set: set[int] = set()
# Queue containing top-level non-trivial T-blossoms.
# The priority of a blossom is its dual plus two times the running
# sum of delta updates.
# sum of delta steps.
self.delta4_queue: PriorityQueue[_NonTrivialBlossom] = PriorityQueue()
# For each T-vertex or unlabeled vertex "x",
@ -677,34 +683,36 @@ class _MatchingContext:
#
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.
"""
"""Assign label S to an unlabeled top-level blossom."""
assert blossom.parent is None
assert blossom.label == _LABEL_NONE
# Assign label S.
blossom.label = _LABEL_S
if isinstance(blossom, _NonTrivialBlossom):
blossom.dual_var -= self.delta_sum_2x
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
blossom.label = _LABEL_NONE
if isinstance(blossom, _NonTrivialBlossom):
blossom.dual_var += self.delta_sum_2x
def assign_vertex_label_s(self, blossom: _Blossom) -> None:
"""Adjust after assigning label S to previously unlabeled vertices."""
# 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.
# Add the new S-vertices to the scan 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
def remove_vertex_label_s(self, blossom: _Blossom) -> None:
"""Adjust after removing labels from S-vertices."""
# Adjust the vertex dual variables of the former S-vertices.
for x in blossom.vertices():
@ -751,12 +759,11 @@ class _MatchingContext:
# Remove blossom labels.
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
if blossom.label == _LABEL_S:
self.remove_blossom_label_s(blossom)
self.remove_vertex_label_s(blossom)
elif blossom.label == _LABEL_T:
self.remove_blossom_label_t(blossom)
blossom.tree_edge = None
# Clear the scan queue.
@ -881,18 +888,20 @@ class _MatchingContext:
# 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.
# Remove blossom labels.
# Mark vertices inside former T-blossoms as S-vertices.
for sub in subblossoms:
if sub.label == _LABEL_T:
if sub.label == _LABEL_S:
self.remove_blossom_label_s(sub)
elif sub.label == _LABEL_T:
self.remove_blossom_label_t(sub)
self.assign_blossom_label_s(sub)
self.assign_vertex_label_s(sub)
# Create the new blossom object.
blossom = _NonTrivialBlossom(subblossoms, path.edges)
# Assign label S to the new blossom and link it to the tree.
blossom.label = _LABEL_S
self.assign_blossom_label_s(blossom)
blossom.tree_edge = subblossoms[0].tree_edge
# Insert into the blossom array.
@ -1185,6 +1194,7 @@ class _MatchingContext:
# Assign label S to the blossom that contains vertex "x".
bx = self.vertex_top_blossom[x]
self.assign_blossom_label_s(bx)
self.assign_vertex_label_s(bx)
y = self.vertex_mate[x]
if y == -1:
@ -1325,7 +1335,7 @@ class _MatchingContext:
elif ylabel == _LABEL_S:
# Update tracking of least-slack edges between S-blossoms.
# Priority is edge slack plus 2 times the running sum of
# delta updates.
# delta steps.
if e not in self.delta3_set:
if self.graph.integer_weights:
# If all edge weights are integers, the slack of
@ -1443,10 +1453,7 @@ class _MatchingContext:
for blossom in self.nontrivial_blossom:
if blossom.parent is None:
blabel = blossom.label
if blabel == _LABEL_S:
# S-blossom: add 2*delta to dual variable.
blossom.dual_var += delta_2x
elif blabel == _LABEL_T:
if blabel == _LABEL_T:
# T-blossom: subtract 2*delta from dual variable.
blossom.dual_var -= delta_2x