KiCad PCB EDA Suite
Loading...
Searching...
No Matches
filter_kruskal.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifndef KICAD_CORE_FILTER_KRUSKAL_H
21#define KICAD_CORE_FILTER_KRUSKAL_H
22
23#include <algorithm>
24#include <cstddef>
25#include <iterator>
26#include <span>
27
28#include <core/union_find.h>
29
43namespace KI_MST
44{
45
47static constexpr size_t KRUSKAL_THRESHOLD = 1024;
48
49
62template <typename EDGE, typename LESS, typename ENDPOINTS, typename EMIT>
63size_t FilterKruskal( std::span<EDGE> aEdges, KI_UNION_FIND& aForest, LESS aLess,
64 ENDPOINTS aEndpoints, EMIT aEmit )
65{
66 size_t emitted = 0;
67
68 // Sort the range and run plain Kruskal over it
69 auto kruskal =
70 [&]( std::span<EDGE> aRange )
71 {
72 std::sort( aRange.begin(), aRange.end(), aLess );
73
74 for( const EDGE& edge : aRange )
75 {
76 const auto [u, v] = aEndpoints( edge );
77
78 if( aForest.Unite( u, v ) )
79 {
80 aEmit( edge );
81 ++emitted;
82 }
83 }
84 };
85
86 // An edge whose endpoints are already joined closes a cycle, so no minimum spanning
87 // forest can hold it and nothing needs to sort it
88 auto filter =
89 [&]( std::span<EDGE> aRange ) -> std::span<EDGE>
90 {
91 auto end = std::partition( aRange.begin(), aRange.end(),
92 [&]( const EDGE& aEdge )
93 {
94 const auto [u, v] = aEndpoints( aEdge );
95
96 return !aForest.Connected( u, v );
97 } );
98
99 return aRange.subspan( 0, std::distance( aRange.begin(), end ) );
100 };
101
102 auto recurse =
103 [&]( auto&& aSelf, std::span<EDGE> aRange ) -> void
104 {
105 // Nothing later can be selected once everything is joined
106 if( aRange.empty() || aForest.ComponentCount() == 1 )
107 return;
108
109 if( aRange.size() <= KRUSKAL_THRESHOLD )
110 {
111 kruskal( aRange );
112 return;
113 }
114
115 // nth_element places the median with the lighter edges ahead of it, so both
116 // halves shrink. It is also deterministic, unlike a random pivot
117 size_t mid = aRange.size() / 2;
118 std::nth_element( aRange.begin(), aRange.begin() + mid, aRange.end(), aLess );
119
120 aSelf( aSelf, aRange.subspan( 0, mid ) );
121
122 if( aForest.ComponentCount() == 1 )
123 return;
124
125 aSelf( aSelf, filter( aRange.subspan( mid ) ) );
126 };
127
128 recurse( recurse, aEdges );
129
130 return emitted;
131}
132
133} // namespace KI_MST
134
135#endif // KICAD_CORE_FILTER_KRUSKAL_H
Lock-free disjoint-set over a dense range of indices.
Definition union_find.h:48
size_t ComponentCount() const
Definition union_find.h:75
bool Unite(size_t aA, size_t aB)
Merge the components that hold aA and aB.
Definition union_find.h:82
Minimum spanning forest by Filter-Kruskal.
size_t FilterKruskal(std::span< EDGE > aEdges, KI_UNION_FIND &aForest, LESS aLess, ENDPOINTS aEndpoints, EMIT aEmit)
Build a minimum spanning forest over aEdges.
static constexpr size_t KRUSKAL_THRESHOLD
< Sort and scan a range of this size or smaller instead of splitting it again.
VECTOR2I end