45# ifndef ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
46# define ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
88 template <
typename WeightType>
91 static_assert(std::numeric_limits<WeightType>::is_specialized,
92 "Edge weight must be a numeric type");
138 return std::make_pair(vt.second, vt.first);
153 template <
typename WeightType>
162 <<
"Vertex ID out of range";
166 not std::isfinite(edge.weight))
167 <<
"Edge weights must be finite numbers";
171 <<
"Edge weight exceeds maximum supported value";
177 <<
"Self-edges are not supported";
185 if (vt.first > vt.second)
186 std::swap(vt.first, vt.second);
192 <<
"Duplicate edges are not supported";
204 template <
typename WeightType>
247 if (edge.weight >= 0)
259 const VertexId m = std::max(edge.vt.first, edge.vt.second);
260 assert(
m < std::numeric_limits<VertexId>::max());
279 nodes[edge.vt.second],
303 template <
typename WeightType>
304 struct NonTrivialBlossom;
314 template <
typename WeightType>
382 template <
typename WeightType>
410 template <
class Blossom_Container,
class Edge_Container>
425 this->subblossoms.get_last().blossom = *
blossom_it;
426 this->subblossoms.get_last().edge = *
edge_it;
436 for (
auto it =
subblossoms.get_it(); it.has_curr(); it.next_ne(), ++pos)
446 template <
typename WeightType,
typename Func>
464 ntb = sub.blossom->nontrivial();
468 func(sub.blossom->base_vertex);
495 template <
typename WeightType>
521 std::numeric_limits<WeightType>::is_integer ? 2 : 1;
653 blossom.best_edge =
nullptr;
657 blossom.best_edge =
nullptr;
658 blossom.best_edge_set.empty();
687 const EdgeT *best_edge =
nullptr;
731 ntb->best_edge_set.append(edge);
758 graph.for_adjacent_edges(sub->base_vertex,
759 [
this,blossom](
const EdgeT *edge)
843 const EdgeT *best_edge =
nullptr;
848 if (blossom->parent ==
nullptr and (blossom->label ==
LABEL_S))
850 const EdgeT *edge = blossom->best_edge;
921 x =
bx->tree_edge.first;
944 y =
by->tree_edge.first;
1001 pos = (pos + 1) % subblossoms.
size();
1011 sub->parent = blossom;
1041 if (&it.get_curr() == blossom)
1095 while (
sub_it.get_pos() != 0)
1110 while (
sub_it.has_curr())
1120 sub_it.get_curr().blossom :
1190 std::tie(x,
y) =
sub_it.get_curr().edge;
1197 std::tie(x,
y) =
sub_it.get_curr().edge;
1201 sub_it.get_curr().blossom :
1224 for (
auto it = blossom->
subblossoms.get_it(); it.has_curr(); it.next_ne())
1225 ring.append(it.get_curr());
1226 const size_t n =
ring.size();
1228 for (
size_t k = 0;
k < n; ++
k)
1364 bx->tree_edge = std::make_pair(
y, x);
1400 while (
ntb !=
nullptr and ntb->dual_var == 0)
1405 ntb =
by->nontrivial();
1410 by->tree_edge = std::make_pair(x,
y);
1484 VertexId y = (edge->
vt.first != x) ? edge->
vt.first : edge->
vt.second;
1561 delta.
value = std::numeric_limits<WeightType>::max();
1591 if (blossom.dual_var / 2 <= delta.
value)
1594 delta.
value = blossom.dual_var / 2;
1619 if (blossom.parent ==
nullptr)
1624 blossom.dual_var += 2 * delta;
1626 else if (blossom.label ==
LABEL_T)
1629 blossom.dual_var -= 2 * delta;
1705 if (delta.
kind == 2)
1715 else if (delta.
kind == 3)
1725 else if (delta.
kind == 4)
1767 template <
typename WeightType>
1777 for (
size_t i = 0; i <
ctx.graph.edges.size(); ++i)
1804 if (a > std::numeric_limits<WeightType>::max() - b)
1813 return edge - &
graph.edges.base();
1827 if (
ctx.vertex_mate[
y] != x)
1835 if (
ctx.vertex_mate[edge.vt.first] == edge.vt.second)
1850 if (
ctx.vertex_dual[x] < 0)
1862 if (blossom.dual_var < 0)
1908 stack.
put(std::make_pair(blossom,
size_t{0}));
1955 stack.
put(std::make_pair(
ntb,
size_t{0}));
1973 if (edge->
vt.first != x)
1991 if (
ctx.vertex_mate[x] ==
y)
2049 ctx.vertex_dual[edge.vt.first],
2050 ctx.vertex_dual[edge.vt.second]))
2058 if (blossom.parent ==
nullptr)
2082 if (
ctx.vertex_mate[edge.vt.first] == edge.vt.second)
2131 template <
typename WeightType>
2143 if (std::numeric_limits<WeightType>::is_integer)
2150 if (
matching.vertex_mate[edge.vt.first] == edge.vt.second)
2151 solution.
append(edge.vt);
2173 template <
typename WeightType>
2181 (std::numeric_limits<WeightType>::is_integer
2182 ? std::numeric_limits<WeightType>::min()
2183 : std::numeric_limits<WeightType>::lowest()) / 4;
2202 const VertexId m = std::max(edge.vt.first, edge.vt.second);
2204 <<
"Vertex ID out of range";
2205 num_vertex = std::max(num_vertex,
m + 1);
2207 if (
not std::numeric_limits<WeightType>::is_integer)
2209 <<
"Edge weights must be finite numbers";
2217 <<
"Edge weight exceeds maximum supported value";
2222 <<
"Adjusted edge weight exceeds maximum supported value";
2244 <<
"Adjusted edge weight exceeds maximum supported value";
2248 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.
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.
and
Check uniqueness with explicit hash + equality functors.
bool all_unique(const Container &container, Equal &eq)
Check if all elements in a container are unique using a comparator.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
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.