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.
|
# 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.
|
# "dual_var" is the current value of the dual variable.
|
||||||
|
#
|
||||||
# New blossoms start with dual variable 0.
|
# New blossoms start with dual variable 0.
|
||||||
self.dual_var: float = 0
|
self.dual_var: float = 0
|
||||||
|
|
||||||
|
@ -542,7 +548,7 @@ class _MatchingContext:
|
||||||
#
|
#
|
||||||
# For an S-vertex "x",
|
# For an S-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"
|
||||||
# plus two times the running sum of delta updates.
|
# plus two times the running sum of delta steps.
|
||||||
#
|
#
|
||||||
# For a T-vertex "x", ... TODO
|
# For a T-vertex "x", ... TODO
|
||||||
self.vertex_dual_2x: list[float]
|
self.vertex_dual_2x: list[float]
|
||||||
|
@ -553,13 +559,13 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Queue containing edges between S-vertices in different top-level
|
# Queue containing edges between S-vertices in different top-level
|
||||||
# blossoms. The priority of an edge is its slack plus two times the
|
# 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_queue: PriorityQueue[int] = PriorityQueue()
|
||||||
self.delta3_set: set[int] = set()
|
self.delta3_set: set[int] = set()
|
||||||
|
|
||||||
# Queue containing top-level non-trivial T-blossoms.
|
# Queue containing top-level non-trivial T-blossoms.
|
||||||
# The priority of a blossom is its dual plus two times the running
|
# 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()
|
self.delta4_queue: PriorityQueue[_NonTrivialBlossom] = PriorityQueue()
|
||||||
|
|
||||||
# For each T-vertex or unlabeled vertex "x",
|
# For each T-vertex or unlabeled vertex "x",
|
||||||
|
@ -677,34 +683,36 @@ class _MatchingContext:
|
||||||
#
|
#
|
||||||
|
|
||||||
def assign_blossom_label_s(self, blossom: _Blossom) -> None:
|
def assign_blossom_label_s(self, blossom: _Blossom) -> None:
|
||||||
"""Assign label S to an unlabeled top-level blossom.
|
"""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.parent is None
|
||||||
assert blossom.label == _LABEL_NONE
|
assert blossom.label == _LABEL_NONE
|
||||||
|
|
||||||
# Assign label S.
|
|
||||||
blossom.label = _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.
|
# Adjust the vertex dual variables of the new S-vertices.
|
||||||
vertices = blossom.vertices()
|
vertices = blossom.vertices()
|
||||||
for x in vertices:
|
for x in vertices:
|
||||||
self.vertex_dual_2x[x] += self.delta_sum_2x
|
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)
|
self.scan_queue.extend(vertices)
|
||||||
|
|
||||||
def remove_blossom_label_s(self, blossom: _Blossom) -> None:
|
def remove_vertex_label_s(self, blossom: _Blossom) -> None:
|
||||||
"""Remove label S from a top-level S-blossom."""
|
"""Adjust after removing labels from S-vertices."""
|
||||||
|
|
||||||
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.
|
# Adjust the vertex dual variables of the former S-vertices.
|
||||||
for x in blossom.vertices():
|
for x in blossom.vertices():
|
||||||
|
@ -751,12 +759,11 @@ 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:
|
||||||
if blossom.label == _LABEL_S:
|
self.remove_blossom_label_s(blossom)
|
||||||
self.remove_blossom_label_s(blossom)
|
self.remove_vertex_label_s(blossom)
|
||||||
elif blossom.label == _LABEL_T:
|
elif blossom.label == _LABEL_T:
|
||||||
self.remove_blossom_label_t(blossom)
|
self.remove_blossom_label_t(blossom)
|
||||||
blossom.label = _LABEL_NONE
|
|
||||||
blossom.tree_edge = None
|
blossom.tree_edge = None
|
||||||
|
|
||||||
# Clear the scan queue.
|
# Clear the scan queue.
|
||||||
|
@ -881,18 +888,20 @@ class _MatchingContext:
|
||||||
# Blossom must start and end with an S-sub-blossom.
|
# Blossom must start and end with an S-sub-blossom.
|
||||||
assert subblossoms[0].label == _LABEL_S
|
assert subblossoms[0].label == _LABEL_S
|
||||||
|
|
||||||
# Relabel the T-sub-blossoms to S-sub-blossoms.
|
# Remove blossom labels.
|
||||||
# This also adds new S-vertices to the scan queue.
|
# Mark vertices inside former T-blossoms as S-vertices.
|
||||||
for sub in subblossoms:
|
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.remove_blossom_label_t(sub)
|
||||||
self.assign_blossom_label_s(sub)
|
self.assign_vertex_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.
|
# 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
|
blossom.tree_edge = subblossoms[0].tree_edge
|
||||||
|
|
||||||
# Insert into the blossom array.
|
# Insert into the blossom array.
|
||||||
|
@ -1185,6 +1194,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]
|
||||||
self.assign_blossom_label_s(bx)
|
self.assign_blossom_label_s(bx)
|
||||||
|
self.assign_vertex_label_s(bx)
|
||||||
|
|
||||||
y = self.vertex_mate[x]
|
y = self.vertex_mate[x]
|
||||||
if y == -1:
|
if y == -1:
|
||||||
|
@ -1325,7 +1335,7 @@ 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
|
# Priority is edge slack plus 2 times the running sum of
|
||||||
# delta updates.
|
# delta steps.
|
||||||
if e not in self.delta3_set:
|
if e not in self.delta3_set:
|
||||||
if self.graph.integer_weights:
|
if self.graph.integer_weights:
|
||||||
# If all edge weights are integers, the slack of
|
# If all edge weights are integers, the slack of
|
||||||
|
@ -1443,10 +1453,7 @@ class _MatchingContext:
|
||||||
for blossom in self.nontrivial_blossom:
|
for blossom in self.nontrivial_blossom:
|
||||||
if blossom.parent is None:
|
if blossom.parent is None:
|
||||||
blabel = blossom.label
|
blabel = blossom.label
|
||||||
if blabel == _LABEL_S:
|
if blabel == _LABEL_T:
|
||||||
# S-blossom: add 2*delta to dual variable.
|
|
||||||
blossom.dual_var += delta_2x
|
|
||||||
elif blabel == _LABEL_T:
|
|
||||||
# T-blossom: subtract 2*delta from dual variable.
|
# T-blossom: subtract 2*delta from dual variable.
|
||||||
blossom.dual_var -= delta_2x
|
blossom.dual_var -= delta_2x
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue