47# ifndef ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
48# define ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
90 template <
typename WeightType>
93 static_assert(std::numeric_limits<WeightType>::is_specialized,
94 "Edge weight must be a numeric type");
140 return std::make_pair(vt.second, vt.first);
155 template <
typename WeightType>
164 <<
"Vertex ID out of range";
168 not std::isfinite(edge.weight))
169 <<
"Edge weights must be finite numbers";
173 <<
"Edge weight exceeds maximum supported value";
179 <<
"Self-edges are not supported";
187 if (vt.first > vt.second)
188 std::swap(vt.first, vt.second);
194 <<
"Duplicate edges are not supported";
206 template <
typename WeightType>
249 if (edge.weight >= 0)
261 const VertexId m = std::max(edge.vt.first, edge.vt.second);
262 assert(
m < std::numeric_limits<VertexId>::max());
281 nodes[edge.vt.second],
305 template <
typename WeightType>
306 struct NonTrivialBlossom;
316 template <
typename WeightType>
384 template <
typename WeightType>
412 template <
class Blossom_Container,
class Edge_Container>
427 this->subblossoms.get_last().blossom = *
blossom_it;
428 this->subblossoms.get_last().edge = *
edge_it;
438 for (
auto it =
subblossoms.get_it(); it.has_curr(); it.next_ne(), ++pos)
448 template <
typename WeightType,
typename Func>
466 ntb = sub.blossom->nontrivial();
470 func(sub.blossom->base_vertex);
497 template <
typename WeightType>
523 std::numeric_limits<WeightType>::is_integer ? 2 : 1;
655 blossom.best_edge =
nullptr;
659 blossom.best_edge =
nullptr;
660 blossom.best_edge_set.empty();
689 const EdgeT *best_edge =
nullptr;
713 assert(blossom->best_edge ==
nullptr);
714 assert((blossom->nontrivial() ==
nullptr)
715 or blossom->nontrivial()->best_edge_set.is_empty());
733 ntb->best_edge_set.append(edge);
760 graph.for_adjacent_edges(sub->base_vertex,
761 [
this,blossom](
const EdgeT *edge)
845 const EdgeT *best_edge =
nullptr;
850 if (blossom->parent ==
nullptr and (blossom->label ==
LABEL_S))
852 const EdgeT *edge = blossom->best_edge;
923 x =
bx->tree_edge.first;
946 y =
by->tree_edge.first;
1003 pos = (pos + 1) % subblossoms.
size();
1013 sub->parent = blossom;
1043 if (&it.get_curr() == blossom)
1097 while (
sub_it.get_pos() != 0)
1112 while (
sub_it.has_curr())
1122 sub_it.get_curr().blossom :
1192 std::tie(x,
y) =
sub_it.get_curr().edge;
1199 std::tie(x,
y) =
sub_it.get_curr().edge;
1203 sub_it.get_curr().blossom :
1226 for (
auto it = blossom->
subblossoms.get_it(); it.has_curr(); it.next_ne())
1227 ring.append(it.get_curr());
1228 const size_t n =
ring.size();
1230 for (
size_t k = 0;
k < n; ++
k)
1366 bx->tree_edge = std::make_pair(
y, x);
1402 while (
ntb !=
nullptr and ntb->dual_var == 0)
1407 ntb =
by->nontrivial();
1412 by->tree_edge = std::make_pair(x,
y);
1486 VertexId y = (edge->
vt.first != x) ? edge->
vt.first : edge->
vt.second;
1559 delta.blossom =
nullptr;
1563 delta.value = std::numeric_limits<WeightType>::max();
1566 delta.value = std::min(delta.value,
vertex_dual[x]);
1573 if (edge !=
nullptr and slack <= delta.value)
1576 delta.value =
slack;
1577 delta.edge = edge->
vt;
1583 if (edge !=
nullptr and slack / 2 <= delta.value)
1586 delta.value =
slack / 2;
1587 delta.edge = edge->
vt;
1593 if (blossom.dual_var / 2 <= delta.value)
1596 delta.value = blossom.dual_var / 2;
1597 delta.blossom = &blossom;
1621 if (blossom.parent ==
nullptr)
1626 blossom.dual_var += 2 * delta;
1628 else if (blossom.label ==
LABEL_T)
1631 blossom.dual_var -= 2 * delta;
1707 if (delta.kind == 2)
1717 else if (delta.kind == 3)
1727 else if (delta.kind == 4)
1769 template <
typename WeightType>
1779 for (
size_t i = 0; i <
ctx.graph.edges.size(); ++i)
1806 if (a > std::numeric_limits<WeightType>::max() - b)
1815 return edge - &
graph.edges.base();
1829 if (
ctx.vertex_mate[
y] != x)
1837 if (
ctx.vertex_mate[edge.vt.first] == edge.vt.second)
1852 if (
ctx.vertex_dual[x] < 0)
1864 if (blossom.dual_var < 0)
1910 stack.
put(std::make_pair(blossom,
size_t{0}));
1957 stack.
put(std::make_pair(
ntb,
size_t{0}));
1975 if (edge->
vt.first != x)
1993 if (
ctx.vertex_mate[x] ==
y)
2051 ctx.vertex_dual[edge.vt.first],
2052 ctx.vertex_dual[edge.vt.second]))
2060 if (blossom.parent ==
nullptr)
2084 if (
ctx.vertex_mate[edge.vt.first] == edge.vt.second)
2133 template <
typename WeightType>
2145 if (std::numeric_limits<WeightType>::is_integer)
2152 if (
matching.vertex_mate[edge.vt.first] == edge.vt.second)
2153 solution.
append(edge.vt);
2175 template <
typename WeightType>
2183 (std::numeric_limits<WeightType>::is_integer
2184 ? std::numeric_limits<WeightType>::min()
2185 : std::numeric_limits<WeightType>::lowest()) / 4;
2204 const VertexId m = std::max(edge.vt.first, edge.vt.second);
2206 <<
"Vertex ID out of range";
2207 num_vertex = std::max(num_vertex,
m + 1);
2209 if (
not std::numeric_limits<WeightType>::is_integer)
2211 <<
"Edge weights must be finite numbers";
2219 <<
"Edge weight exceeds maximum supported value";
2224 <<
"Adjusted edge weight exceeds maximum supported value";
2246 <<
"Adjusted edge weight exceeds maximum supported value";
2250 edge.weight += delta;
Exception handling system with formatted messages for Aleph-w.
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Functional programming utilities for Aleph-w containers.
High-level sorting functions for Aleph containers.
virtual Node * insert_node(Node *p)
Arc * insert_arc(Node *src, Node *tgt, void *a)
Simple dynamic array with automatic resizing and functional operations.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
void empty() noexcept
Empties the container.
constexpr bool is_empty() const noexcept
Checks if the container is empty.
T & get_first() noexcept
return a modifiable reference to the first element.
T & append(const T &data)
Append a copy of data
T & get_last() noexcept
return a modifiable reference to the last element.
void reserve(size_t cap)
Reserves cap cells into the array.
Dynamic doubly linked list with O(1) size and bidirectional access.
T & get_last() const
Return a modifiable reference to last item in the list.
const size_t & size() const noexcept
Return the number of elements (constant time)
T remove_last()
Remove the last item of the list; return a copy of removed item.
T & append(const T &item)
Append a copied item at the end of the list.
T & get_first() const
Return a modifiable reference to first item in the list.
T & insert(const T &item)
Insert a copy of item at the beginning of the list.
T remove_first()
Remove the first item of the list; return a copy of removed item.
Dynamic queue of elements of generic type T based on single linked list.
T & put(const T &data)
The type of element.
T get()
Remove the oldest item of the queue.
void clear() noexcept
Empties the container.
bool is_empty() const noexcept
Return true if this is empty.
Dynamic stack of elements of generic type T based on a singly linked list.
T & top()
Return a modifiable reference to the top item of the stack.
bool is_empty() const noexcept
Check if the stack is empty.
constexpr size_t size() const noexcept
Return the number of elements in the stack.
T get()
Alias for pop() - removes and returns the top item.
T & put(const T &data)
Alias for push() - for compatibility with queue-like interfaces.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Encapsulates the complete state of the weighted matching algorithm.
Array< const EdgeT * > vertex_best_edge
Array< WeightType > vertex_dual
Dual variable for each vertex.
Array< bool > vertex_marker
static void lset_new_blossom(BlossomT *blossom)
Start tracking edges for a new S-blossom.
void make_blossom(const AlternatingPath &path)
Create a new blossom from an alternating cycle.
MatchingContext & operator=(const MatchingContext &)=delete
void lset_merge_blossoms(NonTrivialBlossomT *blossom)
Update least-slack edge tracking after merging sub-blossoms into a new S-blossom.
void expand_unlabeled_blossom(NonTrivialBlossomT *blossom)
Expand the specified unlabeled blossom.
void expand_t_blossom(NonTrivialBlossomT *blossom)
Expand the specified T-blossom.
bool add_s_to_s_edge(VertexId x, VertexId y)
Add the edge between S-vertices "x" and "y".
Array< BlossomT > trivial_blossom
All trivial (single vertex) blossoms.
MatchingContext(const MatchingContext &)=delete
std::pair< const EdgeT *, WeightType > lset_get_best_vertex_edge()
Return the index and slack of the least-slack edge between any S-vertex and unlabeled vertex.
const Problem_Data< WeightType > graph
Internal graph representation.
void augment_matching(const AlternatingPath &path)
Augment the matching through the specified augmenting path.
void substage_apply_delta_step(WeightType delta)
Apply a delta step to the dual LPP variables.
DynDlist< NonTrivialBlossomT > nontrivial_blossom
Currently active non-trivial blossoms.
void augment_blossom_rec(NonTrivialBlossomT *blossom, BlossomT *entry, DynListStack< std::pair< NonTrivialBlossomT *, BlossomT * > > &rec_stack)
Augment along an alternating path through the specified blossom, from sub-blossom "entry" to the base...
void reset_stage()
Reset data which are only valid during a stage.
MatchingContext(const Array< EdgeT > &edges_in)
Initialize context from an edge array.
void lset_add_vertex_edge(VertexId y, const EdgeT *edge, WeightType slack)
Add edge "e" from an S-vertex to unlabeled vertex or T-vertex "y".
WeightType edge_slack(const EdgeT &edge) const
Calculate edge slack.
AlternatingPath trace_alternating_paths(VertexId x, VertexId y)
Trace back through the alternating trees from vertices "x" and "y".
void assign_label_t(VertexId x, VertexId y)
Assign label T to the unlabeled blossom that contains vertex "y".
void run()
Run the matching algorithm.
void assign_label_s(VertexId x)
Assign label S to the unlabeled blossom that contains vertex "x".
Array< VertexId > marked_vertex
bool substage_scan()
Scan queued S-vertices to expand the alternating trees.
std::pair< const EdgeT *, WeightType > lset_get_best_blossom_edge()
Return the index and slack of the least-slack edge between any pair of top-level S-blossoms.
void lset_add_blossom_edge(BlossomT *blossom, const EdgeT *edge, WeightType slack)
Add edge "e" between the specified S-blossom and another S-blossom.
Array< BlossomT * > vertex_top_blossom
Maps each vertex to its highest-level containing blossom.
DeltaStep substage_calc_dual_delta()
Calculate a delta step in the dual LPP problem.
void lset_reset()
Reset least-slack edge tracking.
DynListQueue< VertexId > queue
void erase_nontrivial_blossom(NonTrivialBlossomT *blossom)
Erase the specified non-trivial blossom.
void augment_blossom(NonTrivialBlossomT *blossom, BlossomT *entry)
Augment along an alternating path through the specified blossom, from sub-blossom "entry" to the base...
static constexpr WeightType weight_factor
Integer scaling factor for weight calculations.
Array< VertexId > vertex_mate
The current mate of each vertex, or NO_VERTEX.
bool run_stage()
Run one stage of the matching algorithm.
NonTrivialBlossom< WeightType > NonTrivialBlossomT
Helper class to verify that an optimal solution has been found.
bool verify()
Verify that the optimum solution has been found.
Array< WeightType > edge_duals
For each edge, the sum of duals of its incident vertices and duals of all blossoms that contain the e...
std::size_t edge_index(const EdgeT *edge)
Convert edge pointer to its index in the array "edges".
bool verify_vertex_mate()
Check that the array "vertex_mate" is consistent.
bool check_blossom(const NonTrivialBlossomT *blossom)
Helper function for verifying the solution.
const Problem_Data< WeightType > & graph
Reference to the input graph.
MatchingVerifier(const MatchingContext< WeightType > &ctx)
bool verify_blossom_duals()
Check that blossom dual variables are non-negative.
const MatchingContext< WeightType > & ctx
Reference to the MatchingContext instance.
bool verify_vertex_duals()
Check that vertex dual variables are non-negative, and all unmatched vertices have zero dual.
bool verify_edge_slack()
Check that all edges have non-negative slack, and check that all matched edges have zero slack.
static bool checked_add(WeightType &result, WeightType a, WeightType b)
bool verify_blossoms_and_calc_edge_duals()
Check that all blossoms are full.
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
void for_vertices_in_blossom(const Blossom< WeightType > *blossom, Func func)
Iterate over all base vertices contained within a blossom.
BlossomLabel
Top-level blossoms may be labeled "S" or "T" or unlabeled.
constexpr VertexId NO_VERTEX
Value used to mark an invalid or undefined vertex.
void check_input_graph(const Array< Edge< WeightType > > &edges)
Check that the input is a valid graph.
VertexPair flip_vertex_pair(const VertexPair &vt)
Return a pair of vertices in flipped order.
Array< Edge< WeightType > > adjust_weights_for_maximum_cardinality_matching(const Array< Edge< WeightType > > &edges_in)
Adjust edge weights to prioritize maximum cardinality matching.
unsigned int VertexId
Type representing the unique ID of a vertex.
std::pair< VertexId, VertexId > VertexPair
Type representing a pair of vertices.
Array< VertexPair > maximum_weight_matching(const Array< Edge< WeightType > > &edges)
Compute a maximum-weighted matching in a general undirected graph.
bool all_unique(const Container &container, Equal &eq)
Check if all elements in a container are unique using a comparator.
and
Check uniqueness with explicit hash + equality functors.
DynArray< T > & in_place_sort(DynArray< T > &c, Cmp cmp=Cmp())
Sorts a DynArray in place.
Filtered iterator of adjacent arcs of a node.
Type representing a weighted edge.
Edge(VertexId x, VertexId y, WeightType w)
Construct from vertex IDs and weight.
VertexPair vt
Incident vertices (u, v).
Edge()
Default constructor.
WeightType weight
Edge weight.
Edge(VertexPair vt, WeightType w)
Construct from a VertexPair and weight.
Represents a sequence of edges along an alternating path.
DynDlist< VertexPair > edges
Represents a blossom in the algorithm.
const NonTrivialBlossom< WeightType > * nontrivial() const
Safe downcast to NonTrivialBlossom (const version).
BlossomLabel label
Label S or T if this is part of the current alternating forest.
VertexPair tree_edge
Tree edge attaching this blossom to its parent in the alternating forest.
NonTrivialBlossom< WeightType > * nontrivial()
Safe downcast to NonTrivialBlossom.
bool is_nontrivial_blossom
Flag to distinguish between Blossom and NonTrivialBlossom.
NonTrivialBlossom< WeightType > * parent
Parent in the blossom hierarchy, or null if top-level.
VertexId base_vertex
Index of the base vertex (the unmatched vertex in the blossom).
const Edge< WeightType > * best_edge
Best edge for dual variable calculation (least slack).
Blossom(VertexId base_vertex)
Blossom(const VertexId base_vertex, const bool is_nontrivial_blossom)
A candidate step for dual variable modification.
int kind
Type of delta step (1-4).
VertexPair edge
Edge causing the update (types 2, 3).
NonTrivialBlossomT * blossom
Blossom causing the update (type 4).
WeightType value
Value of the dual update.
VertexPair edge
Edge connecting this sub-blossom to the next in the cycle.
Blossom< WeightType > * blossom
Pointer to sub-blossom.
Represents a non-trivial blossom (an odd cycle of sub-blossoms).
VertexId find_subblossom_pos(Blossom< WeightType > *subblossom) const
DynDlist< SubBlossom > subblossoms
Ring of sub-blossoms forming this blossom.
DynDlist< const Edge< WeightType > * > best_edge_set
WeightType dual_var
Dual variable value for this blossom.
NonTrivialBlossom(const Blossom_Container &subblossoms, const Edge_Container &edges)
Construct a new non-trivial blossom.
Representation of the input graph for the internal solver.
Problem_Data(const Array< EdgeT > &edges_in)
Initialize data and build the internal graph.
const VertexId num_vertex
Number of vertices in the graph.
static Array< EdgeT > remove_negative_weight_edges(const Array< EdgeT > &edges_in)
const Array< EdgeT > edges
Pre-filtered edges (non-negative only).
void build_adjacent_graph()
Array< Internal_Node * > nodes
ID to node mapping.
Internal_Graph adjacent_graph
Adjacency graph used to iterate over incident edges.
Problem_Data(const Problem_Data &)=delete
static VertexId count_num_vertex(const Array< EdgeT > &edges)
void for_adjacent_edges(const VertexId x, Op op) const
Iterates over edges incident to vertex x.
Problem_Data & operator=(const Problem_Data &)=delete
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
Array-based graph implementation.
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Dynamic stack implementation based on linked lists.