Lazy delta updates of S-blossom duals
This commit is contained in:
parent
de03796d99
commit
b2e055b357
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue