diff --git a/.clang-format b/.clang-format index e73f6d56..81a8918f 100644 --- a/.clang-format +++ b/.clang-format @@ -5,6 +5,7 @@ AlignEscapedNewlinesLeft: true AlwaysBreakAfterDefinitionReturnType: None BreakBeforeBraces: Allman BreakConstructorInitializersBeforeComma: false +BreakTemplateDeclarations: Yes ColumnLimit: 80 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4505d4d..fef3d356 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,3 +176,227 @@ jobs: - name: Test run: ..\..\..\b2 --hash address-model=64 cxxstd=14,17,20 toolset=msvc-14.3 working-directory: ../boost-root/libs/graph/test + + posix-cmake-subdir: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-20.04 + - os: ubuntu-22.04 + - os: ubuntu-24.04 + - os: macos-13 + - os: macos-14 + - os: macos-15 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Install packages + if: matrix.install + run: sudo apt-get -y install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + + - name: Use library with add_subdirectory + run: | + cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test + mkdir __build__ && cd __build__ + cmake .. + cmake --build . + ctest --output-on-failure --no-tests=error + + posix-cmake-install: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-20.04 + - os: ubuntu-22.04 + - os: ubuntu-24.04 + - os: macos-13 + - os: macos-14 + - os: macos-15 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Install packages + if: matrix.install + run: sudo apt-get -y install ${{matrix.install}} + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + cd .. + git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + + - name: Configure + run: | + cd ../boost-root + mkdir __build__ && cd __build__ + cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local .. + + - name: Install + run: | + cd ../boost-root/__build__ + cmake --build . --target install + + - name: Use the installed library + run: | + cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__ + cmake -DCMAKE_INSTALL_PREFIX=~/.local .. + cmake --build . + ctest --output-on-failure --no-tests=error + + windows-cmake-subdir: + strategy: + fail-fast: false + matrix: + include: + - os: windows-2019 + - os: windows-2022 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Boost + shell: cmd + run: | + echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% + for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi + echo LIBRARY: %LIBRARY% + echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% + echo GITHUB_BASE_REF: %GITHUB_BASE_REF% + echo GITHUB_REF: %GITHUB_REF% + if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% + set BOOST_BRANCH=develop + for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master + echo BOOST_BRANCH: %BOOST_BRANCH% + cd .. + git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% + + - name: Use library with add_subdirectory (Debug) + shell: cmd + run: | + cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test + mkdir __build__ && cd __build__ + cmake .. + cmake --build . --config Debug + ctest --output-on-failure --no-tests=error -C Debug + + - name: Use library with add_subdirectory (Release) + shell: cmd + run: | + cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test/__build__ + cmake --build . --config Release + ctest --output-on-failure --no-tests=error -C Release + + windows-cmake-install: + strategy: + fail-fast: false + matrix: + include: + - os: windows-2019 + - os: windows-2022 + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Boost + shell: cmd + run: | + echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% + for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi + echo LIBRARY: %LIBRARY% + echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% + echo GITHUB_BASE_REF: %GITHUB_BASE_REF% + echo GITHUB_REF: %GITHUB_REF% + if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% + set BOOST_BRANCH=develop + for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master + echo BOOST_BRANCH: %BOOST_BRANCH% + cd .. + git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ + git submodule update --init tools/boostdep + python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% + + - name: Configure + shell: cmd + run: | + cd ../boost-root + mkdir __build__ && cd __build__ + cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix .. + + - name: Install (Debug) + shell: cmd + run: | + cd ../boost-root/__build__ + cmake --build . --target install --config Debug + + - name: Install (Release) + shell: cmd + run: | + cd ../boost-root/__build__ + cmake --build . --target install --config Release + + - name: Use the installed library (Debug) + shell: cmd + run: | + cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test && mkdir __build__ && cd __build__ + cmake -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix .. + cmake --build . --config Debug + ctest --output-on-failure --no-tests=error -C Debug + + - name: Use the installed library (Release) + shell: cmd + run: | + cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test/__build__ + cmake --build . --config Release + ctest --output-on-failure --no-tests=error -C Release diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e294eb5..73ba0407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,6 @@ target_link_libraries(boost_graph Boost::array Boost::assert Boost::bimap - Boost::bind Boost::concept_check Boost::config Boost::container_hash diff --git a/README.md b/README.md index 9fab5205..1114cbd4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Boost Graph Library [![Build Status](https://drone.cpp.al/api/badges/boostorg/graph/status.svg)](https://drone.cpp.al/boostorg/graph)[![Build Status](https://github.com/boostorg/graph/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/graph/actions) +# Boost Graph Library =================== @@ -6,7 +6,7 @@ A generic interface for traversing graphs, using C++ templates. The full documentation is available on [boost.org](http://www.boost.org/doc/libs/release/libs/graph/doc/index.html). -## Support, bugs and feature requests ## +## Support, bugs and feature requests Bugs and feature requests can be reported through the [Github issue page](https://github.com/boostorg/graph/issues). @@ -22,13 +22,15 @@ You can submit your changes through a [pull request](https://github.com/boostorg There is no mailing-list specific to Boost Graph, although you can use the general-purpose Boost [mailing-list](http://lists.boost.org/mailman/listinfo.cgi/boost-users) using the tag [graph]. -## Development ## +### Build Status | | Master | Develop | |------------------|----------|-------------| -| Github Actions | [![Build Status](https://github.com/boostorg/graph/workflows/CI/badge.svg?branch=master)](https://github.com/boostorg/graph/actions) | [![Build Status](https://github.com/boostorg/graph/workflows/CI/badge.svg?branch=develop)](https://github.com/boostorg/graph/actions) | +| Github Actions | [![Build Status](https://github.com/boostorg/graph/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/boostorg/graph/actions) | [![Build Status](https://github.com/boostorg/graph/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/boostorg/graph/actions) | |Drone | [![Build Status](https://drone.cpp.al/api/badges/boostorg/graph/status.svg?ref=refs/heads/master)](https://drone.cpp.al/boostorg/graph) | [![Build Status](https://drone.cpp.al/api/badges/boostorg/graph/status.svg)](https:/drone.cpp.al/boostorg/graph) | +## Development + Clone the whole boost project, which includes the individual Boost projects as submodules ([see boost+git doc](https://github.com/boostorg/boost/wiki/Getting-Started)): git clone https://github.com/boostorg/boost @@ -45,7 +47,7 @@ Boost Graph Library is mostly made of headers but also contains some compiled co **Note:** The Boost Graph Library cannot currently be built outside of Boost itself. -### Running tests ### +### Running tests First, make sure you are in `libs/graph/test`. You can either run all the 300+ tests listed in `Jamfile.v2` or run a single test: diff --git a/doc/bibliography.html b/doc/bibliography.html index b541abf6..e79333f5 100644 --- a/doc/bibliography.html +++ b/doc/bibliography.html @@ -453,6 +453,11 @@ Journal of the ACM (JACM), 23(2): 221-234, 1976. Data Structures for Weighted Matching and Nearest Common Ancestors with Linking
Proceedings of the First Annual ACM-SIAM Symposium on Discrete Algorithms, pp. 434-443, 1990. +

77 +
Zvi Galil
+Efficient Algorithms for Finding Maximum Matching in Graphs
+ACM Computing Surveys (CSUR), 18(1), 23-38, 1986. +
diff --git a/doc/grid_graph.html b/doc/grid_graph.html index 261dd57c..053ad115 100644 --- a/doc/grid_graph.html +++ b/doc/grid_graph.html @@ -69,7 +69,7 @@

Defined in boost/graph/grid_graph.hpp - with all functions in the boost namespace. A simple examples of creating and iterating over a grid_graph is available here libs/graph/example/grid_graph_example.cpp. An example of adding properties to a grid_graph is also available libs/graph/example/grid_graph_properties.cpp + with all functions in the boost namespace. A simple examples of creating and iterating over a grid_graph is available here libs/graph/example/grid_graph_example.cpp. An example of adding properties to a grid_graph is also available libs/graph/example/grid_graph_properties.cpp

Template Parameters

diff --git a/doc/maximum_weighted_matching.html b/doc/maximum_weighted_matching.html index ab40f1f4..ca3f221a 100644 --- a/doc/maximum_weighted_matching.html +++ b/doc/maximum_weighted_matching.html @@ -1,5 +1,6 @@ " << target(*ei, g) << " with weight of " << weight[*ei] << std::endl; diff --git a/example/kruskal-telephone.cpp b/example/kruskal-telephone.cpp index c88337c5..e4a48ab9 100644 --- a/example/kruskal-telephone.cpp +++ b/example/kruskal-telephone.cpp @@ -30,8 +30,7 @@ int main() property< edge_weight_t, int > > Graph; Graph g(num_vertices(g_dot)); - property_map< GraphvizGraph, edge_attribute_t >::type edge_attr_map - = get(edge_attribute, g_dot); + auto edge_attr_map = get(edge_attribute, g_dot); graph_traits< GraphvizGraph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g_dot); ei != ei_end; ++ei) { @@ -45,22 +44,20 @@ int main() size_type; kruskal_minimum_spanning_tree(g, std::back_inserter(mst)); - property_map< Graph, edge_weight_t >::type weight = get(edge_weight, g); + auto weight = get(edge_weight, g); int total_weight = 0; - for (size_type e = 0; e < mst.size(); ++e) - total_weight += get(weight, mst[e]); + for (auto const& edge : mst) + total_weight += get(weight, edge); std::cout << "total weight: " << total_weight << std::endl; typedef graph_traits< Graph >::vertex_descriptor Vertex; - for (size_type i = 0; i < mst.size(); ++i) + for (auto const& edge : mst) { - Vertex u = source(mst[i], g), v = target(mst[i], g); + auto u = source(edge, g), v = target(edge, g); edge_attr_map[edge(u, v, g_dot).first]["color"] = "black"; } std::ofstream out("figs/telephone-mst-kruskal.dot"); - graph_property< GraphvizGraph, graph_edge_attribute_t >::type& - graph_edge_attr_map - = get_property(g_dot, graph_edge_attribute); + auto graph_edge_attr_map = get_property(g_dot, graph_edge_attribute); graph_edge_attr_map["color"] = "gray"; graph_edge_attr_map["style"] = "bold"; write_graphviz(out, g_dot); diff --git a/example/kuratowski_subgraph.cpp b/example/kuratowski_subgraph.cpp index e9779e63..3ce3f269 100644 --- a/example/kuratowski_subgraph.cpp +++ b/example/kuratowski_subgraph.cpp @@ -45,7 +45,7 @@ int main(int argc, char** argv) add_edge(4, 5, g); // Initialize the interior edge index - property_map< graph, edge_index_t >::type e_index = get(edge_index, g); + auto e_index = get(edge_index, g); graph_traits< graph >::edges_size_type edge_count = 0; graph_traits< graph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) diff --git a/example/last-mod-time.cpp b/example/last-mod-time.cpp index 7d8676eb..7001fe4e 100644 --- a/example/last-mod-time.cpp +++ b/example/last-mod-time.cpp @@ -76,8 +76,7 @@ int main(int argc, const char** argv) exit(-1); } // Obtain internal property map from the graph - property_map< graph_type, vertex_name_t >::type name_map - = get(vertex_name, g); + auto name_map = get(vertex_name, g); read_graph_file(file_in, name_in, g, name_map); // Create storage for last modified times @@ -89,7 +88,7 @@ int main(int argc, const char** argv) // Create last modified time property map iter_map_t mod_time_map(last_mod_vec.begin(), get(vertex_index, g)); - property_map< graph_type, vertex_name_t >::type name = get(vertex_name, g); + auto name = get(vertex_name, g); struct stat stat_buf; graph_traits< graph_type >::vertex_descriptor u; typedef graph_traits< graph_type >::vertex_iterator vertex_iter_t; diff --git a/example/leda-graph-eg.cpp b/example/leda-graph-eg.cpp index 507498ec..008349d9 100644 --- a/example/leda-graph-eg.cpp +++ b/example/leda-graph-eg.cpp @@ -19,7 +19,7 @@ int main() g.new_node("Eurystheus"); g.new_node("Amphitryon"); typedef property_map< graph_t, vertex_all_t >::type NodeMap; - NodeMap node_name_map = get(vertex_all, g); + auto node_name_map = get(vertex_all, g); graph_traits< graph_t >::vertex_iterator vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) std::cout << node_name_map[*vi] << std::endl; diff --git a/example/loops_dfs.cpp b/example/loops_dfs.cpp index b56dd83a..e080307b 100644 --- a/example/loops_dfs.cpp +++ b/example/loops_dfs.cpp @@ -133,20 +133,18 @@ int main(int argc, char* argv[]) typedef std::set< Vertex > set_t; typedef std::list< set_t > list_of_sets_t; list_of_sets_t loops; - Vertex entry = *vertices(g).first; + auto entry = *vertices(g).first; find_loops(entry, g, loops); - property_map< Graph, vertex_attribute_t >::type vattr_map - = get(vertex_attribute, g); - property_map< Graph, edge_attribute_t >::type eattr_map - = get(edge_attribute, g); + auto vattr_map = get(vertex_attribute, g); + auto eattr_map = get(edge_attribute, g); graph_traits< Graph >::edge_iterator ei, ei_end; - for (list_of_sets_t::iterator i = loops.begin(); i != loops.end(); ++i) + for (auto i = loops.begin(); i != loops.end(); ++i) { std::vector< bool > in_loop(num_vertices(g), false); - for (set_t::iterator j = (*i).begin(); j != (*i).end(); ++j) + for (auto j = (*i).begin(); j != (*i).end(); ++j) { vattr_map[*j]["color"] = "gray"; in_loop[*j] = true; @@ -167,9 +165,7 @@ int main(int argc, char* argv[]) for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { loops_out << *vi << "["; - for (std::map< std::string, std::string >::iterator ai - = vattr_map[*vi].begin(); - ai != vattr_map[*vi].end(); ++ai) + for (auto ai = vattr_map[*vi].begin(); ai != vattr_map[*vi].end(); ++ai) { loops_out << ai->first << "=" << ai->second; if (next(ai) != vattr_map[*vi].end()) @@ -181,10 +177,8 @@ int main(int argc, char* argv[]) for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { loops_out << source(*ei, g) << " -> " << target(*ei, g) << "["; - std::map< std::string, std::string >& attr_map = eattr_map[*ei]; - for (std::map< std::string, std::string >::iterator eai - = attr_map.begin(); - eai != attr_map.end(); ++eai) + auto& attr_map = eattr_map[*ei]; + for (auto eai = attr_map.begin(); eai != attr_map.end(); ++eai) { loops_out << eai->first << "=" << eai->second; if (next(eai) != attr_map.end()) diff --git a/example/make_biconnected_planar.cpp b/example/make_biconnected_planar.cpp index f9c101a7..bf9a1d18 100644 --- a/example/make_biconnected_planar.cpp +++ b/example/make_biconnected_planar.cpp @@ -41,7 +41,7 @@ int main(int argc, char** argv) add_edge(0, 10, g); // Initialize the interior edge index - property_map< graph, edge_index_t >::type e_index = get(edge_index, g); + auto e_index = get(edge_index, g); graph_traits< graph >::edges_size_type edge_count = 0; graph_traits< graph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) diff --git a/example/make_maximal_planar.cpp b/example/make_maximal_planar.cpp index 4647f1c0..92b1294d 100644 --- a/example/make_maximal_planar.cpp +++ b/example/make_maximal_planar.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) << 2 * num_vertices(g) - 4 << " faces." << std::endl; // Initialize the interior edge index - property_map< graph, edge_index_t >::type e_index = get(edge_index, g); + auto e_index = get(edge_index, g); graph_traits< graph >::edges_size_type edge_count = 0; graph_traits< graph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) diff --git a/example/matching_example.cpp b/example/matching_example.cpp index 3c8a4fbb..39c9b7cd 100644 --- a/example/matching_example.cpp +++ b/example/matching_example.cpp @@ -25,15 +25,14 @@ int main() const int n_vertices = 18; - std::vector< std::string > ascii_graph; - - ascii_graph.push_back(" 0 1---2 3 "); - ascii_graph.push_back(" \\ / \\ / "); - ascii_graph.push_back(" 4---5 6---7 "); - ascii_graph.push_back(" | | | | "); - ascii_graph.push_back(" 8---9 10---11 "); - ascii_graph.push_back(" / \\ / \\ "); - ascii_graph.push_back(" 12 13 14---15 16 17 "); + std::vector< std::string > ascii_graph + = { " 0 1---2 3 ", + " \\ / \\ / ", + " 4---5 6---7 ", + " | | | | ", + " 8---9 10---11 ", + " / \\ / \\ ", + " 12 13 14---15 16 17 " }; // It has a perfect matching of size 8. There are two isolated // vertices that we'll use later... @@ -75,9 +74,8 @@ int main() std::cout << "In the following graph:" << std::endl << std::endl; - for (std::vector< std::string >::iterator itr = ascii_graph.begin(); - itr != ascii_graph.end(); ++itr) - std::cout << *itr << std::endl; + for (auto const& str : ascii_graph) + std::cout << str << std::endl; std::cout << std::endl << "Found a matching of size " << matching_size(g, &mate[0]) @@ -106,8 +104,7 @@ int main() std::cout << "In the following graph:" << std::endl << std::endl; - for (std::vector< std::string >::iterator itr = ascii_graph.begin(); - itr != ascii_graph.end(); ++itr) + for (auto itr = ascii_graph.begin(); itr != ascii_graph.end(); ++itr) std::cout << *itr << std::endl; std::cout << std::endl diff --git a/example/max_flow.cpp b/example/max_flow.cpp index 17035688..ca7eed5e 100644 --- a/example/max_flow.cpp +++ b/example/max_flow.cpp @@ -62,11 +62,9 @@ int main() Graph g; - property_map< Graph, edge_capacity_t >::type capacity - = get(edge_capacity, g); - property_map< Graph, edge_reverse_t >::type rev = get(edge_reverse, g); - property_map< Graph, edge_residual_capacity_t >::type residual_capacity - = get(edge_residual_capacity, g); + auto capacity = get(edge_capacity, g); + auto rev = get(edge_reverse, g); + auto residual_capacity = get(edge_residual_capacity, g); Traits::vertex_descriptor s, t; read_dimacs_max_flow(g, capacity, rev, s, t); diff --git a/example/mcgregor_subgraphs_example.cpp b/example/mcgregor_subgraphs_example.cpp index f0b74fc9..287a47a9 100644 --- a/example/mcgregor_subgraphs_example.cpp +++ b/example/mcgregor_subgraphs_example.cpp @@ -81,14 +81,12 @@ int main(int argc, char* argv[]) property< edge_name_t, unsigned int > > Graph; - typedef property_map< Graph, vertex_name_t >::type VertexNameMap; - // Test maximum and unique variants on known graphs Graph graph_simple1, graph_simple2; example_callback< Graph > user_callback(graph_simple1); - VertexNameMap vname_map_simple1 = get(vertex_name, graph_simple1); - VertexNameMap vname_map_simple2 = get(vertex_name, graph_simple2); + auto vname_map_simple1 = get(vertex_name, graph_simple1); + auto vname_map_simple2 = get(vertex_name, graph_simple2); // Graph that looks like a triangle put(vname_map_simple1, add_vertex(graph_simple1), 1); diff --git a/example/mean_geodesic.cpp b/example/mean_geodesic.cpp index d0cfee82..7b41bb3d 100644 --- a/example/mean_geodesic.cpp +++ b/example/mean_geodesic.cpp @@ -71,7 +71,7 @@ int main(int argc, char* argv[]) // so-called small-world distance) as a result. GeodesicContainer geodesics(num_vertices(g)); GeodesicMap gm(geodesics, g); - float sw = all_mean_geodesics(g, dm, gm); + auto sw = all_mean_geodesics(g, dm, gm); // Print the mean geodesic distance of each vertex and finally, // the graph itself. diff --git a/example/miles_span.cpp b/example/miles_span.cpp index d09c7f6f..66d7cb9a 100644 --- a/example/miles_span.cpp +++ b/example/miles_span.cpp @@ -102,11 +102,9 @@ int main(int argc, char* argv[]) long sp_length = 0; // Use the "z" utility field for distance. - typedef property_map< Graph*, z_property< long > >::type Distance; - Distance d = get(z_property< long >(), g); + auto d = get(z_property< long >(), g); // Use the "w" property for parent - typedef property_map< Graph*, w_property< Vertex* > >::type Parent; - Parent p = get(w_property< Vertex* >(), g); + auto p = get(w_property< Vertex* >(), g); total_length_visitor< Distance > length_vis(sp_length, d); prim_minimum_spanning_tree(g, p, diff --git a/example/min_max_paths.cpp b/example/min_max_paths.cpp index fabf4c94..a35bc4cc 100644 --- a/example/min_max_paths.cpp +++ b/example/min_max_paths.cpp @@ -52,14 +52,14 @@ int main(int, char*[]) const char name[] = "abcdef"; const int num_nodes = 6; - E edges[] = { E(0, 2), E(1, 1), E(1, 3), E(1, 4), E(2, 1), E(2, 3), E(3, 4), - E(4, 0), E(4, 1) }; + const auto edges = { E(0, 2), E(1, 1), E(1, 3), E(1, 4), E(2, 1), E(2, 3), + E(3, 4), E(4, 0), E(4, 1) }; int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1 }; const int n_edges = sizeof(edges) / sizeof(E); #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 // VC++ can't handle iterator constructors Graph G(num_nodes); - property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, G); + auto weightmap = get(edge_weight, G); for (std::size_t j = 0; j < sizeof(edges) / sizeof(E); ++j) { graph_traits< Graph >::edge_descriptor e; @@ -68,8 +68,8 @@ int main(int, char*[]) weightmap[e] = weights[j]; } #else - Graph G(edges, edges + n_edges, weights, num_nodes); - property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, G); + Graph G(std::begin(edges), std::end(edges), weights, num_nodes); + auto = get(edge_weight, G); #endif std::vector< Vertex > p(num_vertices(G)); diff --git a/example/minimum_degree_ordering.cpp b/example/minimum_degree_ordering.cpp index c096ccf6..38044a16 100644 --- a/example/minimum_degree_ordering.cpp +++ b/example/minimum_degree_ordering.cpp @@ -146,8 +146,7 @@ int main(int argc, char* argv[]) Vector supernode_sizes(n, 1); // init has to be 1 - boost::property_map< Graph, vertex_index_t >::type id - = get(vertex_index, G); + auto id = get(vertex_index, G); Vector degree(n, 0); diff --git a/example/modify_graph.cpp b/example/modify_graph.cpp index 9c8836e0..acad2c47 100644 --- a/example/modify_graph.cpp +++ b/example/modify_graph.cpp @@ -47,8 +47,7 @@ template < class MutableGraph > void modify_demo(MutableGraph& g) typename GraphTraits::edges_size_type m = 0; typename GraphTraits::vertex_descriptor u, v, w; edge_descriptor e, e1, e2; - typename property_map< MutableGraph, edge_name_t >::type name_map - = get(edge_name, g); + auto name_map = get(edge_name, g); bool added; typename GraphTraits::vertex_iterator vi, vi_end; diff --git a/example/neighbor_bfs.cpp b/example/neighbor_bfs.cpp index 2beb4b6e..ca3e2d76 100644 --- a/example/neighbor_bfs.cpp +++ b/example/neighbor_bfs.cpp @@ -65,16 +65,14 @@ public: template < class Edge, class Graph > void tree_out_edge(Edge e, const Graph& g) const { - typename graph_traits< Graph >::vertex_descriptor u = source(e, g), - v = target(e, g); + auto u = source(e, g), v = target(e, g); put(m_distance, v, get(m_distance, u) + 1); put(m_predecessor, v, u); } template < class Edge, class Graph > void tree_in_edge(Edge e, const Graph& g) const { - typename graph_traits< Graph >::vertex_descriptor u = source(e, g), - v = target(e, g); + auto u = source(e, g), v = target(e, g); put(m_distance, u, get(m_distance, v) + 1); put(m_predecessor, u, v); } diff --git a/example/ordered_out_edges.cpp b/example/ordered_out_edges.cpp index 5235eb28..16003f88 100644 --- a/example/ordered_out_edges.cpp +++ b/example/ordered_out_edges.cpp @@ -112,8 +112,8 @@ int main() add_edge(3, 4, EdgeProperty("harry"), g); add_edge(0, 1, EdgeProperty("chandler"), g); - property_map< graph_type, vertex_index_t >::type id = get(vertex_index, g); - property_map< graph_type, edge_name_t >::type name = get(edge_name, g); + auto id = get(vertex_index, g); + auto name = get(edge_name, g); graph_traits< graph_type >::vertex_iterator i, end; graph_traits< graph_type >::out_edge_iterator ei, edge_end; diff --git a/example/ospf-example.cpp b/example/ospf-example.cpp index 54d30a1d..d0b44df2 100644 --- a/example/ospf-example.cpp +++ b/example/ospf-example.cpp @@ -49,7 +49,7 @@ int main(int argc, const char** argv) graph_traits< g_dot_type >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g_dot); ei != ei_end; ++ei) { - int weight = get(edge_weight, g_dot, *ei); + auto weight = get(edge_weight, g_dot, *ei); property< edge_weight_t, int > edge_property(weight); add_edge(source(*ei, g_dot), target(*ei, g_dot), edge_property, g); } @@ -71,8 +71,8 @@ int main(int argc, const char** argv) #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 std::vector< int > distance(num_vertices(g)); - property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, g); - property_map< Graph, vertex_index_t >::type indexmap = get(vertex_index, g); + auto weightmap = get(edge_weight, g); + auto indexmap = get(vertex_index, g); dijkstra_shortest_paths(g, router_six, &parent[0], &distance[0], weightmap, indexmap, std::less< int >(), closed_plus< int >(), (std::numeric_limits< int >::max)(), 0, default_dijkstra_visitor()); diff --git a/example/planar_face_traversal.cpp b/example/planar_face_traversal.cpp index f67e34c0..e96e9c3b 100644 --- a/example/planar_face_traversal.cpp +++ b/example/planar_face_traversal.cpp @@ -71,7 +71,7 @@ int main(int argc, char** argv) add_edge(5, 8, g); // Initialize the interior edge index - property_map< graph, edge_index_t >::type e_index = get(edge_index, g); + auto e_index = get(edge_index, g); graph_traits< graph >::edges_size_type edge_count = 0; graph_traits< graph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) diff --git a/example/prim-example.cpp b/example/prim-example.cpp index d9a78758..7136a484 100644 --- a/example/prim-example.cpp +++ b/example/prim-example.cpp @@ -18,12 +18,12 @@ int main() Graph; typedef std::pair< int, int > E; const int num_nodes = 5; - E edges[] + const auto edges = { E(0, 2), E(1, 3), E(1, 4), E(2, 1), E(2, 3), E(3, 4), E(4, 0) }; int weights[] = { 1, 1, 2, 7, 3, 1, 1 }; #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 Graph g(num_nodes); - property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, g); + auto weightmap = get(edge_weight, g); for (std::size_t j = 0; j < sizeof(edges) / sizeof(E); ++j) { graph_traits< Graph >::edge_descriptor e; @@ -32,14 +32,13 @@ int main() weightmap[e] = weights[j]; } #else - Graph g(edges, edges + sizeof(edges) / sizeof(E), weights, num_nodes); + Graph g(std::begin(edges), std::end(edges), weights, num_nodes); #endif std::vector< graph_traits< Graph >::vertex_descriptor > p(num_vertices(g)); #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - property_map< Graph, vertex_distance_t >::type distance - = get(vertex_distance, g); - property_map< Graph, vertex_index_t >::type indexmap = get(vertex_index, g); + auto distance = get(vertex_distance, g); + auto indexmap = get(vertex_index, g); prim_minimum_spanning_tree(g, *vertices(g).first, &p[0], distance, weightmap, indexmap, default_dijkstra_visitor()); #else diff --git a/example/prim-telephone.cpp b/example/prim-telephone.cpp index 1f256db0..e5dd4df9 100644 --- a/example/prim-telephone.cpp +++ b/example/prim-telephone.cpp @@ -30,21 +30,20 @@ int main() property< edge_weight_t, int > > Graph; Graph g(num_vertices(g_dot)); - property_map< GraphvizGraph, edge_attribute_t >::type edge_attr_map - = get(edge_attribute, g_dot); + auto edge_attr_map = get(edge_attribute, g_dot); graph_traits< GraphvizGraph >::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = edges(g_dot); ei != ei_end; ++ei) { - int weight = lexical_cast< int >(edge_attr_map[*ei]["label"]); + auto weight = lexical_cast< int >(edge_attr_map[*ei]["label"]); property< edge_weight_t, int > edge_property(weight); add_edge(source(*ei, g_dot), target(*ei, g_dot), edge_property, g); } typedef graph_traits< Graph >::vertex_descriptor Vertex; std::vector< Vertex > parent(num_vertices(g)); - property_map< Graph, edge_weight_t >::type weight = get(edge_weight, g); + auto weight = get(edge_weight, g); #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - property_map< Graph, vertex_index_t >::type indexmap = get(vertex_index, g); + auto indexmap = get(vertex_index, g); std::vector< std::size_t > distance(num_vertices(g)); prim_minimum_spanning_tree(g, *vertices(g).first, &parent[0], &distance[0], weight, indexmap, default_dijkstra_visitor()); @@ -53,12 +52,12 @@ int main() #endif int total_weight = 0; - for (int v = 0; v < num_vertices(g); ++v) + for (std::size_t v = 0; v < num_vertices(g); ++v) if (parent[v] != v) total_weight += get(weight, edge(parent[v], v, g).first); std::cout << "total weight: " << total_weight << std::endl; - for (int u = 0; u < num_vertices(g); ++u) + for (std::size_t u = 0; u < num_vertices(g); ++u) if (parent[u] != u) edge_attr_map[edge(parent[u], u, g_dot).first]["color"] = "black"; std::ofstream out("figs/telephone-mst-prim.dot"); diff --git a/example/property-map-traits-eg.cpp b/example/property-map-traits-eg.cpp index 3df92432..7c86caac 100644 --- a/example/property-map-traits-eg.cpp +++ b/example/property-map-traits-eg.cpp @@ -17,7 +17,7 @@ int main() graph_t; graph_t g; graph_traits< graph_t >::vertex_descriptor u = add_vertex(g); - property_map< graph_t, vertex_name_t >::type name_map = get(vertex_name, g); + auto name_map = get(vertex_name, g); name_map[u] = "Joe"; std::cout << name_map[u] << std::endl; return EXIT_SUCCESS; diff --git a/example/property_iterator.cpp b/example/property_iterator.cpp index e95619b3..286b81b1 100644 --- a/example/property_iterator.cpp +++ b/example/property_iterator.cpp @@ -106,14 +106,12 @@ int main(int argc, char* argv[]) std::cout << write(graph); std::cout << "radii:" << std::endl; - graph_property_iter_range< Graph, radius_t >::type seqRadius - = get_property_iter_range(graph, radius_t()); + auto seqRadius = get_property_iter_range(graph, radius_t()); std::for_each(seqRadius.first, seqRadius.second, Print()); std::cout << std::endl; std::cout << "stiff:" << std::endl; - graph_property_iter_range< Graph, stiff_t >::type seqStiff - = get_property_iter_range(graph, stiff_t()); + auto seqStiff = get_property_iter_range(graph, stiff_t()); std::for_each(seqStiff.first, seqStiff.second, Print()); std::cout << std::endl; diff --git a/example/push-relabel-eg.cpp b/example/push-relabel-eg.cpp index 04a6fa48..cf146de1 100644 --- a/example/push-relabel-eg.cpp +++ b/example/push-relabel-eg.cpp @@ -56,20 +56,17 @@ int main() Graph; Graph g; - property_map< Graph, edge_capacity_t >::type capacity - = get(edge_capacity, g); - property_map< Graph, edge_residual_capacity_t >::type residual_capacity - = get(edge_residual_capacity, g); - property_map< Graph, edge_reverse_t >::type rev = get(edge_reverse, g); + auto capacity = get(edge_capacity, g); + auto residual_capacity = get(edge_residual_capacity, g); + auto rev = get(edge_reverse, g); Traits::vertex_descriptor s, t; read_dimacs_max_flow(g, capacity, rev, s, t); #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - property_map< Graph, vertex_index_t >::type indexmap = get(vertex_index, g); - long flow = push_relabel_max_flow( + auto flow = push_relabel_max_flow( g, s, t, capacity, residual_capacity, rev, indexmap); #else - long flow = push_relabel_max_flow(g, s, t); + auto flow = push_relabel_max_flow(g, s, t); #endif std::cout << "c The total flow:" << std::endl; diff --git a/example/quick-tour.cpp b/example/quick-tour.cpp index 8d79b72b..85f3c518 100644 --- a/example/quick-tour.cpp +++ b/example/quick-tour.cpp @@ -32,8 +32,7 @@ void print_vertex_names(const Graph& g, VertexNameMap name_map) { std::cout << "vertices(g) = { "; typedef typename graph_traits< Graph >::vertex_iterator iter_t; - for (std::pair< iter_t, iter_t > p = vertices(g); p.first != p.second; - ++p.first) + for (auto p = vertices(g); p.first != p.second; ++p.first) { print_vertex_name(*p.first, name_map); std::cout << ' '; @@ -95,9 +94,8 @@ int main() graph_t; graph_t g; - property_map< graph_t, vertex_name_t >::type name_map = get(vertex_name, g); - property_map< graph_t, edge_weight_t >::type delay_map - = get(edge_weight, g); + auto name_map = get(vertex_name, g); + auto delay_map = get(edge_weight, g); build_router_network(g, name_map, delay_map); print_vertex_names(g, name_map); diff --git a/example/quick_tour.cpp b/example/quick_tour.cpp index 25d1f5e1..f952c976 100644 --- a/example/quick_tour.cpp +++ b/example/quick_tour.cpp @@ -24,8 +24,7 @@ template < class Graph > struct exercise_vertex void operator()(const Vertex& v) const { using namespace boost; - typename property_map< Graph, vertex_index_t >::type vertex_id - = get(vertex_index, g); + auto vertex_id = get(vertex_index, g); std::cout << "vertex: " << name[get(vertex_id, v)] << std::endl; // Write out the outgoing edges @@ -36,7 +35,7 @@ template < class Graph > struct exercise_vertex ++out_i) { e = *out_i; - Vertex src = source(e, g), targ = target(e, g); + auto src = source(e, g), targ = target(e, g); std::cout << "(" << name[get(vertex_id, src)] << "," << name[get(vertex_id, targ)] << ") "; } @@ -106,7 +105,7 @@ int main(int, char*[]) #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 // VC++ can't handle the iterator constructor Graph g(num_vertices); - property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, g); + auto weightmap = get(edge_weight, g); for (std::size_t j = 0; j < num_edges; ++j) { graph_traits< Graph >::edge_descriptor e; diff --git a/example/reachable-loop-head.cpp b/example/reachable-loop-head.cpp index fcb90ff1..aa05ab0a 100644 --- a/example/reachable-loop-head.cpp +++ b/example/reachable-loop-head.cpp @@ -42,8 +42,7 @@ int main(int argc, char* argv[]) make_iterator_property_map( reachable_from_head.begin(), get(vertex_index, g), c)); - property_map< GraphvizDigraph, vertex_attribute_t >::type vattr_map - = get(vertex_attribute, g); + auto vattr_map = get(vertex_attribute, g); graph_traits< GraphvizDigraph >::vertex_iterator i, i_end; for (boost::tie(i, i_end) = vertices(g); i != i_end; ++i) @@ -64,9 +63,7 @@ int main(int argc, char* argv[]) for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { loops_out << *vi << "["; - for (std::map< std::string, std::string >::iterator ai - = vattr_map[*vi].begin(); - ai != vattr_map[*vi].end(); ++ai) + for (auto ai = vattr_map[*vi].begin(); ai != vattr_map[*vi].end(); ++ai) { loops_out << ai->first << "=" << ai->second; if (next(ai) != vattr_map[*vi].end()) @@ -81,9 +78,7 @@ int main(int argc, char* argv[]) { loops_out << source(*ei, g) << " -> " << target(*ei, g) << "["; std::map< std::string, std::string >& attr_map = eattr_map[*ei]; - for (std::map< std::string, std::string >::iterator eai - = attr_map.begin(); - eai != attr_map.end(); ++eai) + for (auto eai = attr_map.begin(); eai != attr_map.end(); ++eai) { loops_out << eai->first << "=" << eai->second; if (next(eai) != attr_map.end()) diff --git a/example/reachable-loop-tail.cpp b/example/reachable-loop-tail.cpp index e3e50b0d..2d9f3dd3 100644 --- a/example/reachable-loop-tail.cpp +++ b/example/reachable-loop-tail.cpp @@ -56,8 +56,7 @@ int main(int argc, char* argv[]) << " node [shape=\"box\"];\n" << " edge [style=\"bold\"];\n"; - property_map< Graph, vertex_attribute_t >::type vattr_map - = get(vertex_attribute, g); + auto vattr_map = get(vertex_attribute, g); graph_traits< GraphvizDigraph >::vertex_iterator i, i_end; for (boost::tie(i, i_end) = vertices(g_in); i != i_end; ++i) { diff --git a/example/read_graphviz.cpp b/example/read_graphviz.cpp index d3b39559..3b6348a4 100644 --- a/example/read_graphviz.cpp +++ b/example/read_graphviz.cpp @@ -35,15 +35,13 @@ int main() graph_t graph(0); dynamic_properties dp; - property_map< graph_t, vertex_name_t >::type name = get(vertex_name, graph); + auto name = get(vertex_name, graph); dp.property("node_id", name); - property_map< graph_t, vertex_color_t >::type mass - = get(vertex_color, graph); + auto mass = get(vertex_color, graph); dp.property("mass", mass); - property_map< graph_t, edge_weight_t >::type weight - = get(edge_weight, graph); + auto weight = get(edge_weight, graph); dp.property("weight", weight); // Use ref_property_map to turn a graph property into a property map diff --git a/example/read_write_dimacs-eg.cpp b/example/read_write_dimacs-eg.cpp index d6435679..9d7f670c 100644 --- a/example/read_write_dimacs-eg.cpp +++ b/example/read_write_dimacs-eg.cpp @@ -78,10 +78,8 @@ int main() typedef property_map< Graph, edge_capacity_t >::type tCapMap; typedef tCapMap::value_type tCapMapValue; - typedef property_map< Graph, edge_reverse_t >::type tRevEdgeMap; - - tCapMap capacity = get(edge_capacity, g); - tRevEdgeMap rev = get(edge_reverse, g); + auto capacity = get(edge_capacity, g); + auto rev = get(edge_reverse, g); vertex_descriptor s, t; /*reading the graph from stdin*/ @@ -95,8 +93,8 @@ int main() out_edge_iterator oei, oe_end; for (boost::tie(oei, oe_end) = out_edges(s, g); oei != oe_end; ++oei) { - edge_descriptor from_source = *oei; - vertex_descriptor v = target(from_source, g); + auto from_source = *oei; + auto v = target(from_source, g); edge_descriptor to_sink; bool is_there; boost::tie(to_sink, is_there) = edge(v, t, g); @@ -104,7 +102,7 @@ int main() { if (get(capacity, to_sink) > get(capacity, from_source)) { - tCapMapValue to_augment = get(capacity, from_source); + auto to_augment = get(capacity, from_source); capacity[from_source] = 0; capacity[to_sink] -= to_augment; augmented_flow += to_augment; diff --git a/example/remove_edge_if_bidir.cpp b/example/remove_edge_if_bidir.cpp index a58df802..9be499fd 100644 --- a/example/remove_edge_if_bidir.cpp +++ b/example/remove_edge_if_bidir.cpp @@ -50,7 +50,7 @@ struct has_weight_greater_than bool operator()(graph_traits< Graph >::edge_descriptor e) { #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - property_map< Graph, edge_weight_t >::type weight = get(edge_weight, g); + auto weight = get(edge_weight, g); return get(weight, e) > w; #else // This version of get() breaks VC++ @@ -81,7 +81,7 @@ int main() for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) weight[*ei] = ++w; - property_map< Graph, vertex_index_t >::type indexmap = get(vertex_index, g); + auto indexmap = get(vertex_index, g); std::cout << "original graph:" << std::endl; print_graph(g, indexmap); diff --git a/example/remove_edge_if_dir.cpp b/example/remove_edge_if_dir.cpp index 825e409b..534f83f9 100644 --- a/example/remove_edge_if_dir.cpp +++ b/example/remove_edge_if_dir.cpp @@ -41,17 +41,10 @@ typedef adjacency_list< vecS, vecS, directedS > Graph; int main() { typedef std::pair< std::size_t, std::size_t > Edge; - Edge edges[6] = { Edge(0, 3), Edge(0, 2), Edge(0, 3), Edge(1, 3), + const auto edges = { Edge(0, 3), Edge(0, 2), Edge(0, 3), Edge(1, 3), Edge(2, 0), Edge(3, 2) }; -#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - // VC++ can't handle iterator constructor - Graph g(4); - for (std::size_t j = 0; j < 6; ++j) - add_edge(edges[j].first, edges[j].second, g); -#else - Graph g(edges, edges + 6, 4); -#endif + Graph g(std::begin(edges), std::end(edges), 4); std::cout << "original graph:" << std::endl; print_graph(g, get(vertex_index, g)); diff --git a/example/remove_edge_if_undir.cpp b/example/remove_edge_if_undir.cpp index 3f4b62e8..0d5f49b3 100644 --- a/example/remove_edge_if_undir.cpp +++ b/example/remove_edge_if_undir.cpp @@ -49,7 +49,7 @@ struct has_weight_greater_than bool operator()(graph_traits< Graph >::edge_descriptor e) { #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - property_map< Graph, edge_weight_t >::type weight = get(edge_weight, g); + auto weight = get(edge_weight, g); return get(weight, e) > w; #else // This version of get breaks VC++ diff --git a/example/roget_components.cpp b/example/roget_components.cpp index 5347acca..a90b1a9d 100644 --- a/example/roget_components.cpp +++ b/example/roget_components.cpp @@ -63,11 +63,9 @@ int main(int argc, char* argv[]) // a separate field for marking colors, so we use the w field. std::vector< int > comp(num_vertices(g)); - property_map< Graph*, vertex_index_t >::type index_map - = get(vertex_index, g); + auto index_map = get(vertex_index, g); - property_map< Graph*, v_property< vertex_t > >::type root - = get(v_property< vertex_t >(), g); + auto root = get(v_property< vertex_t >(), g); int num_comp = strong_components(g, make_iterator_property_map(comp.begin(), index_map), @@ -131,8 +129,8 @@ int main(int argc, char* argv[]) graph_traits< Graph* >::out_edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = out_edges(v, g); ei != ei_end; ++ei) { - vertex_t x = target(*ei, g); - int comp_x = comp[index_map[x]]; + auto x = target(*ei, g); + auto comp_x = comp[index_map[x]]; if (comp_x != c && mark[comp_x] != c) { mark[comp_x] = c; diff --git a/example/scc.cpp b/example/scc.cpp index 142316db..94dc7d08 100644 --- a/example/scc.cpp +++ b/example/scc.cpp @@ -31,8 +31,7 @@ int main() strong_components(g, make_assoc_property_map(component)); - property_map< GraphvizDigraph, vertex_attribute_t >::type vertex_attr_map - = get(vertex_attribute, g); + auto vertex_attr_map = get(vertex_attribute, g); std::string color[] = { "white", "gray", "black", "lightgray" }; graph_traits< GraphvizDigraph >::vertex_iterator vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) diff --git a/example/sloan_ordering.cpp b/example/sloan_ordering.cpp index 538e1060..f3b98d1e 100644 --- a/example/sloan_ordering.cpp +++ b/example/sloan_ordering.cpp @@ -106,13 +106,12 @@ int main(int, char*[]) graph_traits< Graph >::vertex_iterator ui, ui_end; // Creating a property_map with the degrees of the degrees of each vertex - property_map< Graph, vertex_degree_t >::type deg = get(vertex_degree, G); + auto deg = get(vertex_degree, G); for (boost::tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui) deg[*ui] = degree(*ui, G); // Creating a property_map for the indices of a vertex - property_map< Graph, vertex_index_t >::type index_map - = get(vertex_index, G); + auto index_map = get(vertex_index, G); std::cout << "original bandwidth: " << bandwidth(G) << std::endl; std::cout << "original profile: " << profile(G) << std::endl; @@ -147,8 +146,7 @@ int main(int, char*[]) cout << "Sloan ordering starting at: " << s << endl; cout << " "; - for (std::vector< Vertex >::const_iterator i = sloan_order.begin(); - i != sloan_order.end(); ++i) + for (auto i = sloan_order.begin(); i != sloan_order.end(); ++i) cout << index_map[*i] << " "; cout << endl; @@ -192,8 +190,7 @@ int main(int, char*[]) cout << endl << "Sloan ordering without a start-vertex:" << endl; cout << " "; - for (std::vector< Vertex >::const_iterator i = sloan_order.begin(); - i != sloan_order.end(); ++i) + for (auto i = sloan_order.begin(); i != sloan_order.end(); ++i) cout << index_map[*i] << " "; cout << endl; diff --git a/example/stoer_wagner.cpp b/example/stoer_wagner.cpp index ce5b0e59..54a2c25c 100644 --- a/example/stoer_wagner.cpp +++ b/example/stoer_wagner.cpp @@ -45,7 +45,7 @@ int main() // construct the graph object. 8 is the number of vertices, which are // numbered from 0 through 7, and 16 is the number of edges. - undirected_graph g(edges, edges + 16, ws, 8, 16); + undirected_graph g(std::begin(edges), std::end(edges), ws, 8, 16); // define a property map, `parities`, that will store a boolean value for // each vertex. Vertices that have the same parity after diff --git a/example/strong-components.cpp b/example/strong-components.cpp index 12265daf..4e5d876b 100644 --- a/example/strong-components.cpp +++ b/example/strong-components.cpp @@ -31,8 +31,7 @@ int main() G, make_iterator_property_map(c.begin(), get(vertex_index, G), c[0])); std::cout << "Total number of components: " << num << std::endl; - std::vector< int >::iterator i; - for (i = c.begin(); i != c.end(); ++i) + for (auto i = c.begin(); i != c.end(); ++i) std::cout << "Vertex " << i - c.begin() << " is in component " << *i << std::endl; return EXIT_SUCCESS; diff --git a/example/subgraph_properties.cpp b/example/subgraph_properties.cpp index ac9316fa..17d217f8 100644 --- a/example/subgraph_properties.cpp +++ b/example/subgraph_properties.cpp @@ -60,7 +60,7 @@ int main(int, char*[]) F }; // for conveniently refering to vertices in G0 - property_map< Graph, vertex_name_t >::type name = get(vertex_name_t(), G0); + auto name = get(vertex_name_t(), G0); name[A] = "A"; name[B] = "B"; name[C] = "C"; @@ -80,7 +80,7 @@ int main(int, char*[]) add_vertex(E, G1); // global vertex E becomes local B1 for G1 add_vertex(F, G1); // global vertex F becomes local C1 for G1 - property_map< Graph, vertex_name_t >::type name1 = get(vertex_name_t(), G1); + auto name1 = get(vertex_name_t(), G1); name1[A1] = "A1"; std::cout << std::endl @@ -120,7 +120,7 @@ int main(int, char*[]) add_vertex(A, G2); // global vertex A becomes local A2 for G2 add_vertex(C, G2); // global vertex C becomes local B2 for G2 - property_map< Graph, vertex_name_t >::type name2 = get(vertex_name_t(), G2); + auto name2 = get(vertex_name_t(), G2); name2[A2] = "A2"; std::cout << std::endl diff --git a/example/successive_shortest_path_nonnegative_weights_example.cpp b/example/successive_shortest_path_nonnegative_weights_example.cpp index 01797203..0b0fb7fd 100644 --- a/example/successive_shortest_path_nonnegative_weights_example.cpp +++ b/example/successive_shortest_path_nonnegative_weights_example.cpp @@ -20,7 +20,7 @@ int main() boost::successive_shortest_path_nonnegative_weights(g, s, t); - int cost = boost::find_flow_cost(g); + auto cost = boost::find_flow_cost(g); assert(cost == 29); return 0; diff --git a/example/tiernan_print_cycles.cpp b/example/tiernan_print_cycles.cpp index 0905973a..e07c53fa 100644 --- a/example/tiernan_print_cycles.cpp +++ b/example/tiernan_print_cycles.cpp @@ -28,9 +28,7 @@ template < typename OutputStream > struct cycle_printer { // Get the property map containing the vertex indices // so we can print them. - typedef - typename property_map< Graph, vertex_index_t >::const_type IndexMap; - IndexMap indices = get(vertex_index, g); + auto indices = get(vertex_index, g); // Iterate over path printing each vertex that forms the cycle. typename Path::const_iterator i, end = p.end(); diff --git a/example/topo-sort-with-leda.cpp b/example/topo-sort-with-leda.cpp index 42c07e2e..d4120b69 100644 --- a/example/topo-sort-with-leda.cpp +++ b/example/topo-sort-with-leda.cpp @@ -45,8 +45,7 @@ int main() std::reverse(topo_order.begin(), topo_order.end()); int n = 1; - for (std::vector< vertex_t >::iterator i = topo_order.begin(); - i != topo_order.end(); ++i, ++n) + for (auto i = topo_order.begin(); i != topo_order.end(); ++i, ++n) std::cout << n << ": " << leda_g[*i] << std::endl; return EXIT_SUCCESS; diff --git a/example/topo-sort-with-sgb.cpp b/example/topo-sort-with-sgb.cpp index 7453c3d3..eb43a89f 100644 --- a/example/topo-sort-with-sgb.cpp +++ b/example/topo-sort-with-sgb.cpp @@ -36,8 +36,7 @@ int main() topological_sort(sgb_g, std::back_inserter(topo_order), vertex_index_map(get(vertex_index, sgb_g))); int n = 1; - for (std::vector< vertex_t >::reverse_iterator i = topo_order.rbegin(); - i != topo_order.rend(); ++i, ++n) + for (auto i = topo_order.rbegin(); i != topo_order.rend(); ++i, ++n) std::cout << n << ": " << tasks[get(vertex_index, sgb_g)[*i]] << std::endl; diff --git a/example/topo-sort1.cpp b/example/topo-sort1.cpp index 6579d0e0..9b5b0dd2 100644 --- a/example/topo-sort1.cpp +++ b/example/topo-sort1.cpp @@ -19,26 +19,17 @@ int main() = { "pick up kids from school", "buy groceries (and snacks)", "get cash at ATM", "drop off kids at soccer practice", "cook dinner", "pick up kids from soccer", "eat dinner" }; - const int n_tasks = sizeof(tasks) / sizeof(char*); - std::vector< std::list< int > > g(n_tasks); - g[0].push_back(3); - g[1].push_back(3); - g[1].push_back(4); - g[2].push_back(1); - g[3].push_back(5); - g[4].push_back(6); - g[5].push_back(6); + std::vector< std::list< int > > g + = { { 3 }, { 3, 4 }, { 1 }, { 5 }, { 6 }, { 6 }, {} }; std::deque< int > topo_order; topological_sort(g, std::front_inserter(topo_order), vertex_index_map(identity_property_map())); - int n = 1; - for (std::deque< int >::iterator i = topo_order.begin(); - i != topo_order.end(); ++i, ++n) - std::cout << tasks[*i] << std::endl; + for (auto const& vertex : topo_order) + std::cout << tasks[vertex] << std::endl; return EXIT_SUCCESS; } diff --git a/example/topo-sort2.cpp b/example/topo-sort2.cpp index 80aa3412..941abcb4 100644 --- a/example/topo-sort2.cpp +++ b/example/topo-sort2.cpp @@ -35,10 +35,8 @@ int main() topological_sort(g, std::front_inserter(topo_order), vertex_index_map(identity_property_map())); - int n = 1; - for (std::deque< int >::iterator i = topo_order.begin(); - i != topo_order.end(); ++i, ++n) - std::cout << tasks[*i] << std::endl; + for (auto const& vertex : topo_order) + std::cout << tasks[vertex] << std::endl; return EXIT_SUCCESS; } diff --git a/example/topo_sort.cpp b/example/topo_sort.cpp index 38dc4667..398814a4 100644 --- a/example/topo_sort.cpp +++ b/example/topo_sort.cpp @@ -43,26 +43,19 @@ int main(int, char*[]) Graph; typedef boost::graph_traits< Graph >::vertex_descriptor Vertex; - Pair edges[6] = { Pair(0, 1), Pair(2, 4), Pair(2, 5), Pair(0, 3), + const auto edges = { Pair(0, 1), Pair(2, 4), Pair(2, 5), Pair(0, 3), Pair(1, 4), Pair(4, 3) }; -#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - // VC++ can't handle the iterator constructor - Graph G(6); - for (std::size_t j = 0; j < 6; ++j) - add_edge(edges[j].first, edges[j].second, G); -#else - Graph G(edges, edges + 6, 6); -#endif - boost::property_map< Graph, vertex_index_t >::type id - = get(vertex_index, G); + Graph G(std::begin(edges), std::end(edges), 6 /* vertices count */); + + auto id = get(vertex_index, G); typedef std::vector< Vertex > container; container c; topological_sort(G, std::back_inserter(c)); std::cout << "A topological ordering: "; - for (container::reverse_iterator ii = c.rbegin(); ii != c.rend(); ++ii) + for (auto ii = c.rbegin(); ii != c.rend(); ++ii) std::cout << id[*ii] << " "; std::cout << std::endl; diff --git a/example/transpose-example.cpp b/example/transpose-example.cpp index 618a288b..3f7d8fe1 100644 --- a/example/transpose-example.cpp +++ b/example/transpose-example.cpp @@ -30,7 +30,7 @@ int main() N }; graph_t G(N); - property_map< graph_t, vertex_name_t >::type name_map = get(vertex_name, G); + auto name_map = get(vertex_name, G); char name = 'a'; graph_traits< graph_t >::vertex_iterator v, v_end; for (boost::tie(v, v_end) = vertices(G); v != v_end; ++v, ++name) diff --git a/example/two_graphs_common_spanning_trees.cpp b/example/two_graphs_common_spanning_trees.cpp index 824556db..0a209bf4 100644 --- a/example/two_graphs_common_spanning_trees.cpp +++ b/example/two_graphs_common_spanning_trees.cpp @@ -36,22 +36,15 @@ typedef boost::graph_traits< Graph >::edge_iterator edge_iterator; int main(int argc, char** argv) { Graph iG, vG; - vector< edge_descriptor > iG_o; - vector< edge_descriptor > vG_o; + vector< edge_descriptor > iG_o = { boost::add_edge(0, 1, iG).first, + boost::add_edge(0, 2, iG).first, boost::add_edge(0, 3, iG).first, + boost::add_edge(0, 4, iG).first, boost::add_edge(1, 2, iG).first, + boost::add_edge(3, 4, iG).first }; - iG_o.push_back(boost::add_edge(0, 1, iG).first); - iG_o.push_back(boost::add_edge(0, 2, iG).first); - iG_o.push_back(boost::add_edge(0, 3, iG).first); - iG_o.push_back(boost::add_edge(0, 4, iG).first); - iG_o.push_back(boost::add_edge(1, 2, iG).first); - iG_o.push_back(boost::add_edge(3, 4, iG).first); - - vG_o.push_back(boost::add_edge(1, 2, vG).first); - vG_o.push_back(boost::add_edge(2, 0, vG).first); - vG_o.push_back(boost::add_edge(2, 3, vG).first); - vG_o.push_back(boost::add_edge(4, 3, vG).first); - vG_o.push_back(boost::add_edge(0, 3, vG).first); - vG_o.push_back(boost::add_edge(0, 4, vG).first); + vector< edge_descriptor > vG_o = { boost::add_edge(1, 2, vG).first, + boost::add_edge(2, 0, vG).first, boost::add_edge(2, 3, vG).first, + boost::add_edge(4, 3, vG).first, boost::add_edge(0, 3, vG).first, + boost::add_edge(0, 4, vG).first }; vector< bool > inL(iG_o.size(), false); @@ -62,8 +55,7 @@ int main(int argc, char** argv) boost::two_graphs_common_spanning_trees( iG, iG_o, vG, vG_o, tree_collector, inL); - std::vector< std::vector< bool > >::iterator it; - for (it = coll.begin(); it != coll.end(); ++it) + for (auto const & vec : coll) { // Here you can play with the trees that the algorithm has found. } diff --git a/example/undirected_adjacency_list.cpp b/example/undirected_adjacency_list.cpp index 9e360149..4836d50e 100644 --- a/example/undirected_adjacency_list.cpp +++ b/example/undirected_adjacency_list.cpp @@ -14,13 +14,12 @@ template < typename UndirectedGraph > void undirected_graph_demo1() { const int V = 3; UndirectedGraph undigraph(V); - typename graph_traits< UndirectedGraph >::vertex_descriptor zero, one, two; typename graph_traits< UndirectedGraph >::out_edge_iterator out, out_end; typename graph_traits< UndirectedGraph >::in_edge_iterator in, in_end; - zero = vertex(0, undigraph); - one = vertex(1, undigraph); - two = vertex(2, undigraph); + auto zero = vertex(0, undigraph); + auto one = vertex(1, undigraph); + auto two = vertex(2, undigraph); add_edge(zero, one, undigraph); add_edge(zero, two, undigraph); add_edge(one, two, undigraph); @@ -39,15 +38,13 @@ template < typename DirectedGraph > void directed_graph_demo() { const int V = 2; DirectedGraph digraph(V); - typename graph_traits< DirectedGraph >::vertex_descriptor u, v; typedef typename DirectedGraph::edge_property_type Weight; - typename property_map< DirectedGraph, edge_weight_t >::type weight - = get(edge_weight, digraph); + auto weight = get(edge_weight, digraph); typename graph_traits< DirectedGraph >::edge_descriptor e1, e2; bool found; - u = vertex(0, digraph); - v = vertex(1, digraph); + auto u = vertex(0, digraph); + auto v = vertex(1, digraph); add_edge(u, v, Weight(1.2), digraph); add_edge(v, u, Weight(2.4), digraph); boost::tie(e1, found) = edge(u, v, digraph); @@ -68,15 +65,14 @@ template < typename UndirectedGraph > void undirected_graph_demo2() { const int V = 2; UndirectedGraph undigraph(V); - typename graph_traits< UndirectedGraph >::vertex_descriptor u, v; typedef typename UndirectedGraph::edge_property_type Weight; typename property_map< UndirectedGraph, edge_weight_t >::type weight = get(edge_weight, undigraph); typename graph_traits< UndirectedGraph >::edge_descriptor e1, e2; bool found; - u = vertex(0, undigraph); - v = vertex(1, undigraph); + auto u = vertex(0, undigraph); + auto v = vertex(1, undigraph); add_edge(u, v, Weight(3.1), undigraph); boost::tie(e1, found) = edge(u, v, undigraph); boost::tie(e2, found) = edge(v, u, undigraph); @@ -92,8 +88,7 @@ template < typename UndirectedGraph > void undirected_graph_demo2() std::cout << "the edges incident to v: "; typename boost::graph_traits< UndirectedGraph >::out_edge_iterator e, e_end; - typename boost::graph_traits< UndirectedGraph >::vertex_descriptor s - = vertex(0, undigraph); + auto s = vertex(0, undigraph); for (boost::tie(e, e_end) = out_edges(s, undigraph); e != e_end; ++e) std::cout << "(" << source(*e, undigraph) << "," << target(*e, undigraph) << ")" << std::endl; diff --git a/example/undirected_graph.cpp b/example/undirected_graph.cpp index 0c0365ce..3fa2847c 100644 --- a/example/undirected_graph.cpp +++ b/example/undirected_graph.cpp @@ -18,9 +18,9 @@ int main(int, char*[]) Graph g; // Add vertices - boost::graph_traits< Graph >::vertex_descriptor v0 = g.add_vertex(); - boost::graph_traits< Graph >::vertex_descriptor v1 = g.add_vertex(); - boost::graph_traits< Graph >::vertex_descriptor v2 = g.add_vertex(); + auto v0 = g.add_vertex(); + auto v1 = g.add_vertex(); + auto v2 = g.add_vertex(); // Add edges g.add_edge(v0, v1); diff --git a/example/vector_as_graph.cpp b/example/vector_as_graph.cpp index f9e785ed..0000a0fc 100644 --- a/example/vector_as_graph.cpp +++ b/example/vector_as_graph.cpp @@ -27,22 +27,18 @@ int main() v, w, x, - y, - N + y }; char name[] = "rstuvwxy"; typedef std::vector< std::list< int > > Graph; - Graph g(N); - g[r].push_back(v); - g[s].push_back(r); - g[s].push_back(r); - g[s].push_back(w); - g[t].push_back(x); - g[u].push_back(t); - g[w].push_back(t); - g[w].push_back(x); - g[x].push_back(y); - g[y].push_back(u); + Graph g = { { v }, // r + { r, r, w }, // s + { x }, // t + { t }, // u + {}, // v + { t, x }, // w + { y }, // x + { u } }; // y boost::print_graph(g, name); return 0; } diff --git a/example/vertex-name-property.cpp b/example/vertex-name-property.cpp index c1a3b0b7..0971334c 100644 --- a/example/vertex-name-property.cpp +++ b/example/vertex-name-property.cpp @@ -67,8 +67,7 @@ int main(int argc, const char** argv) } // Obtain internal property map from the graph - property_map< graph_type, vertex_name_t >::type name_map - = get(vertex_name, g); + auto name_map = get(vertex_name, g); read_graph_file(file_in, name_in, g, name_map); // Create storage for last modified times diff --git a/example/vertex_basics.cpp b/example/vertex_basics.cpp index 107fd5eb..4cbc0bf3 100644 --- a/example/vertex_basics.cpp +++ b/example/vertex_basics.cpp @@ -61,11 +61,10 @@ template < class Graph > struct print_edge typedef typename boost::graph_traits< Graph >::vertex_descriptor Vertex; void operator()(Edge e) const { - typename boost::property_map< Graph, vertex_index_t >::type id - = get(vertex_index, G); + auto id = get(vertex_index, G); - Vertex src = source(e, G); - Vertex targ = target(e, G); + auto src = source(e, G); + auto targ = target(e, G); cout << "(" << id[src] << "," << id[targ] << ") "; } diff --git a/example/vf2_sub_graph_iso_multi_example.cpp b/example/vf2_sub_graph_iso_multi_example.cpp index 330fe28c..235df83d 100644 --- a/example/vf2_sub_graph_iso_multi_example.cpp +++ b/example/vf2_sub_graph_iso_multi_example.cpp @@ -69,16 +69,9 @@ int main() add_edge(5, 0, edge_property('s'), graph2); // create predicates - typedef property_map< graph_type, vertex_name_t >::type vertex_name_map_t; - typedef property_map_equivalent< vertex_name_map_t, vertex_name_map_t > - vertex_comp_t; - vertex_comp_t vertex_comp = make_property_map_equivalent( + auto vertex_comp = make_property_map_equivalent( get(vertex_name, graph1), get(vertex_name, graph2)); - - typedef property_map< graph_type, edge_name_t >::type edge_name_map_t; - typedef property_map_equivalent< edge_name_map_t, edge_name_map_t > - edge_comp_t; - edge_comp_t edge_comp = make_property_map_equivalent( + auto edge_comp = make_property_map_equivalent( get(edge_name, graph1), get(edge_name, graph2)); // Create callback diff --git a/example/visitor.cpp b/example/visitor.cpp index 8620c116..75b83e30 100644 --- a/example/visitor.cpp +++ b/example/visitor.cpp @@ -71,15 +71,9 @@ int main(int, char*[]) typedef adjacency_list<> Graph; typedef std::pair< int, int > E; - E edges[] = { E(0, 2), E(1, 1), E(1, 3), E(2, 1), E(2, 3), E(3, 1), E(3, 4), - E(4, 0), E(4, 1) }; -#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 - Graph G(5); - for (std::size_t j = 0; j < sizeof(edges) / sizeof(E); ++j) - add_edge(edges[j].first, edges[j].second, G); -#else - Graph G(edges, edges + sizeof(edges) / sizeof(E), 5); -#endif + const auto edges = { E(0, 2), E(1, 1), E(1, 3), E(2, 1), E(2, 3), E(3, 1), + E(3, 4), E(4, 0), E(4, 1) }; + Graph G(std::begin(edges), std::end(edges), 5); typedef boost::graph_traits< Graph >::vertices_size_type size_type; diff --git a/include/boost/graph/adjacency_list.hpp b/include/boost/graph/adjacency_list.hpp index e5050b5b..113143a7 100644 --- a/include/boost/graph/adjacency_list.hpp +++ b/include/boost/graph/adjacency_list.hpp @@ -267,18 +267,21 @@ template < class OutEdgeListS = vecS, // a Sequence or an AssociativeContainer class EdgeProperty = no_property, class GraphProperty = no_property, class EdgeListS = listS > class adjacency_list -: public detail::adj_list_gen< - adjacency_list< OutEdgeListS, VertexListS, DirectedS, VertexProperty, - EdgeProperty, GraphProperty, EdgeListS >, - VertexListS, OutEdgeListS, DirectedS, VertexProperty, EdgeProperty, - GraphProperty, EdgeListS >::type, - // Support for named vertices +: // Support for named vertices + // This needs to be inherited from first to ensure it's initialized before the + // "base" (i.e. detail::adj_list_gen), because the "base" might indirectly + // call functions on it during its construction. public graph::maybe_named_graph< adjacency_list< OutEdgeListS, VertexListS, DirectedS, VertexProperty, EdgeProperty, GraphProperty, EdgeListS >, typename adjacency_list_traits< OutEdgeListS, VertexListS, DirectedS, EdgeListS >::vertex_descriptor, - VertexProperty > + VertexProperty >, + public detail::adj_list_gen< + adjacency_list< OutEdgeListS, VertexListS, DirectedS, VertexProperty, + EdgeProperty, GraphProperty, EdgeListS >, + VertexListS, OutEdgeListS, DirectedS, VertexProperty, EdgeProperty, + GraphProperty, EdgeListS >::type { public: typedef GraphProperty graph_property_type; diff --git a/include/boost/graph/adjacency_matrix.hpp b/include/boost/graph/adjacency_matrix.hpp index a089e1cb..83c22de5 100644 --- a/include/boost/graph/adjacency_matrix.hpp +++ b/include/boost/graph/adjacency_matrix.hpp @@ -433,7 +433,8 @@ struct adj_matrix_traversal_tag : public virtual adjacency_matrix_tag, public virtual vertex_list_graph_tag, public virtual incidence_graph_tag, public virtual adjacency_graph_tag, - public virtual edge_list_graph_tag + public virtual edge_list_graph_tag, + public virtual bidirectional_graph_tag { }; @@ -826,6 +827,27 @@ typename adjacency_matrix< D, VP, EP, GP, A >::degree_size_type in_degree( return n; } +// O(N) +template < typename VP, typename EP, typename GP, typename A > +typename adjacency_matrix< directedS, VP, EP, GP, A >::degree_size_type +degree( + typename adjacency_matrix< directedS, VP, EP, GP, A >::vertex_descriptor u, + const adjacency_matrix< directedS, VP, EP, GP, A >& g) +{ + return in_degree(u, g) + out_degree(u, g); +} + +// O(N) +template < typename VP, typename EP, typename GP, typename A > +typename adjacency_matrix< undirectedS, VP, EP, GP, A >::degree_size_type +degree( + typename adjacency_matrix< undirectedS, VP, EP, GP, A >::vertex_descriptor + u, + const adjacency_matrix< undirectedS, VP, EP, GP, A >& g) +{ + return out_degree(u, g); +} + //========================================================================= // Functions required by the AdjacencyGraph concept diff --git a/include/boost/graph/bron_kerbosch_all_cliques.hpp b/include/boost/graph/bron_kerbosch_all_cliques.hpp index e0f862e8..28bc10f2 100644 --- a/include/boost/graph/bron_kerbosch_all_cliques.hpp +++ b/include/boost/graph/bron_kerbosch_all_cliques.hpp @@ -104,7 +104,7 @@ struct max_clique_visitor max_clique_visitor(std::size_t& max) : maximum(max) {} template < typename Clique, typename Graph > - inline void clique(const Clique& p, const Graph& g) + inline void clique(const Clique& p, const Graph&) { BOOST_USING_STD_MAX(); maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION(maximum, p.size()); @@ -220,7 +220,7 @@ namespace detail // otherwise, iterate over candidates and and test // for maxmimal cliquiness. - typename Container::iterator i, j; + typename Container::iterator i; for (i = cands.begin(); i != cands.end();) { Vertex candidate = *i; diff --git a/include/boost/graph/cycle_canceling.hpp b/include/boost/graph/cycle_canceling.hpp index 5aaa25e7..c355db51 100644 --- a/include/boost/graph/cycle_canceling.hpp +++ b/include/boost/graph/cycle_canceling.hpp @@ -172,7 +172,7 @@ namespace detail template < class Graph, class P, class T, class R > void cycle_canceling(Graph& g, const bgl_named_params< P, T, R >& params) { - cycle_canceling_dispatch1(g, + detail::cycle_canceling_dispatch1(g, choose_const_pmap(get_param(params, edge_weight), g, edge_weight), choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse), choose_pmap(get_param(params, edge_residual_capacity), g, diff --git a/include/boost/graph/detail/adjacency_list.hpp b/include/boost/graph/detail/adjacency_list.hpp index ed5a1d48..5358fdba 100644 --- a/include/boost/graph/detail/adjacency_list.hpp +++ b/include/boost/graph/detail/adjacency_list.hpp @@ -2228,9 +2228,10 @@ inline typename Config::vertex_descriptor add_vertex( vec_adj_list_impl< Graph, Config, Base >& g_) { Graph& g = static_cast< Graph& >(g_); - g.m_vertices.resize(g.m_vertices.size() + 1); - g.added_vertex(g.m_vertices.size() - 1); - return g.m_vertices.size() - 1; + auto const added_descriptor = g.m_vertices.size(); + g.m_vertices.emplace_back(); + g.added_vertex(added_descriptor); + return added_descriptor; } template < class Graph, class Config, class Base > @@ -2243,10 +2244,10 @@ inline typename Config::vertex_descriptor add_vertex( if (optional< vertex_descriptor > v = g.vertex_by_property(get_property_value(p, vertex_bundle))) return *v; - typedef typename Config::stored_vertex stored_vertex; - g.m_vertices.push_back(stored_vertex(p)); - g.added_vertex(g.m_vertices.size() - 1); - return g.m_vertices.size() - 1; + auto const added_descriptor = g.m_vertices.size(); + g.m_vertices.emplace_back(p); + g.added_vertex(added_descriptor); + return added_descriptor; } // Here we override the directed_graph_helper add_edge() function diff --git a/include/boost/graph/dominator_tree.hpp b/include/boost/graph/dominator_tree.hpp index df9783b0..afe741fc 100644 --- a/include/boost/graph/dominator_tree.hpp +++ b/include/boost/graph/dominator_tree.hpp @@ -10,8 +10,8 @@ #define BOOST_GRAPH_DOMINATOR_HPP #include -#include #include +#include #include #include @@ -62,6 +62,21 @@ namespace detail Tag >(timeMap, v, t); } + // Auxiliary structure of different kinds of predecessors are used to + // calculate the semidominators: ancestor, semidominator, and the ancestor + // with the lowest semidominator (`best`). Placing these predecessors in a + // structure let us organize a "vector of structs" what improves cache + // efficiency. + template < class Graph > + struct vertex_triple + { + using Vertex = typename graph_traits< Graph >::vertex_descriptor; + + Vertex semi { graph_traits< Graph >::null_vertex() }; + Vertex ancestor { graph_traits< Graph >::null_vertex() }; + Vertex best { graph_traits< Graph >::null_vertex() }; + }; + template < class Graph, class IndexMap, class TimeMap, class PredMap, class DomTreePredMap > class dominator_visitor @@ -80,13 +95,9 @@ namespace detail */ dominator_visitor(const Graph& g, const Vertex& entry, const IndexMap& indexMap, DomTreePredMap domTreePredMap) - : semi_(num_vertices(g)) - , ancestor_(num_vertices(g), graph_traits< Graph >::null_vertex()) - , samedom_(ancestor_) - , best_(semi_) - , semiMap_(make_iterator_property_map(semi_.begin(), indexMap)) - , ancestorMap_(make_iterator_property_map(ancestor_.begin(), indexMap)) - , bestMap_(make_iterator_property_map(best_.begin(), indexMap)) + : pred_(num_vertices(g)) + , predMap_(make_iterator_property_map(pred_.begin(), indexMap)) + , samedom_(num_vertices(g), graph_traits< Graph >::null_vertex()) , buckets_(num_vertices(g)) , bucketMap_(make_iterator_property_map(buckets_.begin(), indexMap)) , entry_(entry) @@ -132,18 +143,18 @@ namespace detail if (get(dfnumMap, v) <= get(dfnumMap, n)) s2 = v; else - s2 = get(semiMap_, ancestor_with_lowest_semi_(v, dfnumMap)); + s2 = get(predMap_, ancestor_with_lowest_semi_(v, dfnumMap)) + .semi; if (get(dfnumMap, s2) < get(dfnumMap, s)) s = s2; } - put(semiMap_, n, s); + auto& pred_of_n = get(predMap_, n); + pred_of_n = {s, p, n}; // 2. Calculation of n's dominator is deferred until // the path from s to n has been linked into the forest get(bucketMap_, s).push_back(n); - get(ancestorMap_, n) = p; - get(bestMap_, n) = n; // 3. Now that the path from p to v has been linked into // the spanning forest, these lines calculate the dominator of v, @@ -155,13 +166,13 @@ namespace detail // // idom(n) = semi(n) if semi(y)=semi(n) or // idom(y) if semi(y) != semi(n) - typename std::deque< Vertex >::iterator buckItr; + typename std::vector< Vertex >::iterator buckItr; for (buckItr = get(bucketMap_, p).begin(); buckItr != get(bucketMap_, p).end(); ++buckItr) { const Vertex v(*buckItr); const Vertex y(ancestor_with_lowest_semi_(v, dfnumMap)); - if (get(semiMap_, y) == get(semiMap_, v)) + if (get(predMap_, y).semi == get(predMap_, v).semi) put(domTreePredMap_, v, p); else put(samedomMap, v, y); @@ -177,28 +188,36 @@ namespace detail const Vertex ancestor_with_lowest_semi_( const Vertex& v, const TimeMap& dfnumMap) { - const Vertex a(get(ancestorMap_, v)); + const Vertex a(get(predMap_, v).ancestor); + const auto& pred_of_a = get(predMap_, a); - if (get(ancestorMap_, a) != graph_traits< Graph >::null_vertex()) + auto& pred_of_v = get(predMap_, v); + + if (pred_of_a.ancestor != graph_traits< Graph >::null_vertex()) { const Vertex b(ancestor_with_lowest_semi_(a, dfnumMap)); + const auto& pred_of_b = get(predMap_, b); - put(ancestorMap_, v, get(ancestorMap_, a)); + pred_of_v.ancestor = pred_of_a.ancestor; - if (get(dfnumMap, get(semiMap_, b)) - < get(dfnumMap, get(semiMap_, get(bestMap_, v)))) - put(bestMap_, v, b); + if (get(dfnumMap, pred_of_b.semi) + < get(dfnumMap, get(predMap_, pred_of_v.best).semi)) + pred_of_v.best = b; } - return get(bestMap_, v); + return pred_of_v.best; } - std::vector< Vertex > semi_, ancestor_, samedom_, best_; - PredMap semiMap_, ancestorMap_, bestMap_; - std::vector< std::deque< Vertex > > buckets_; + std::vector< vertex_triple< Graph > > pred_; + iterator_property_map< typename std::vector< vertex_triple< Graph > >::iterator, + IndexMap > + predMap_; + + std::vector< Vertex > samedom_; + std::vector< std::vector< Vertex > > buckets_; iterator_property_map< - typename std::vector< std::deque< Vertex > >::iterator, IndexMap > + typename std::vector< std::vector< Vertex > >::iterator, IndexMap > bucketMap_; const Vertex& entry_; diff --git a/include/boost/graph/graph_traits.hpp b/include/boost/graph/graph_traits.hpp index 208cef1f..ea865217 100644 --- a/include/boost/graph/graph_traits.hpp +++ b/include/boost/graph/graph_traits.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/graph/howard_cycle_ratio.hpp b/include/boost/graph/howard_cycle_ratio.hpp index fe6c5bd9..8163e63b 100644 --- a/include/boost/graph/howard_cycle_ratio.hpp +++ b/include/boost/graph/howard_cycle_ratio.hpp @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -241,14 +240,10 @@ namespace detail typename graph_traits< Graph >::out_edge_iterator oei, oeie; for (boost::tie(vi, vie) = vertices(m_g); vi != vie; ++vi) { - using namespace boost::placeholders; - boost::tie(oei, oeie) = out_edges(*vi, m_g); - typename graph_traits< Graph >::out_edge_iterator mei - = boost::first_max_element(oei, oeie, - boost::bind(m_cmp, - boost::bind(&EdgeWeight1::operator[], m_ew1m, _1), - boost::bind(&EdgeWeight1::operator[], m_ew1m, _2))); + auto mei = boost::first_max_element(oei, oeie, + [this](const auto& first, const auto& second) + { return m_cmp(m_ew1m[first], m_ew1m[second]); }); if (mei == oeie) { if (m_sink == graph_traits< Graph >().null_vertex()) @@ -356,16 +351,17 @@ namespace detail */ float_t policy_mcr() { - using namespace boost::placeholders; - std::fill(m_col_bfs.begin(), m_col_bfs.end(), my_white); color_map_t vcm_ = color_map_t(m_col_bfs.begin(), m_vim); typename graph_traits< Graph >::vertex_iterator uv_itr, vie; boost::tie(uv_itr, vie) = vertices(m_g); float_t mcr = m_bound; while ((uv_itr = std::find_if(uv_itr, vie, - boost::bind(std::equal_to< my_color_type >(), my_white, - boost::bind(&color_map_t::operator[], vcm_, _1)))) + [this, &vcm_](const auto& uv) + { + return std::equal_to< my_color_type >()( + my_white, vcm_[uv]); + })) != vie) /// While there are undiscovered vertices { diff --git a/include/boost/graph/is_straight_line_drawing.hpp b/include/boost/graph/is_straight_line_drawing.hpp index 013f4b40..d7701540 100644 --- a/include/boost/graph/is_straight_line_drawing.hpp +++ b/include/boost/graph/is_straight_line_drawing.hpp @@ -16,74 +16,44 @@ #include #include +#include +#include +#include + +#include + #include #include -#include #include namespace boost { - -// Return true exactly when the line segments s1 = ((x1,y1), (x2,y2)) and -// s2 = ((a1,b1), (a2,b2)) intersect in a point other than the endpoints of -// the line segments. The one exception to this rule is when s1 = s2, in -// which case false is returned - this is to accomodate multiple edges -// between the same pair of vertices, which shouldn't invalidate the straight -// line embedding. A tolerance variable epsilon can also be used, which -// defines how far away from the endpoints of s1 and s2 we want to consider -// an intersection. - -inline bool intersects(double x1, double y1, double x2, double y2, double a1, - double b1, double a2, double b2, double epsilon = 0.000001) +// Overload of make from Boost.Geometry. +template +Geometry make(typename graph_traits::edge_descriptor e, + Graph const &g, + GridPositionMap const &drawing) { + auto e_source(source(e, g)); + auto e_target(target(e, g)); + using Float = typename geometry::coordinate_type::type; + return {{numeric_cast(drawing[e_source].x), numeric_cast(drawing[e_source].y)}, + {numeric_cast(drawing[e_target].x), numeric_cast(drawing[e_target].y)}}; +} - if (x1 - x2 == 0) - { - std::swap(x1, a1); - std::swap(y1, b1); - std::swap(x2, a2); - std::swap(y2, b2); - } - - if (x1 - x2 == 0) - { - BOOST_USING_STD_MAX(); - BOOST_USING_STD_MIN(); - - // two vertical line segments - double min_y = min BOOST_PREVENT_MACRO_SUBSTITUTION(y1, y2); - double max_y = max BOOST_PREVENT_MACRO_SUBSTITUTION(y1, y2); - double min_b = min BOOST_PREVENT_MACRO_SUBSTITUTION(b1, b2); - double max_b = max BOOST_PREVENT_MACRO_SUBSTITUTION(b1, b2); - if ((max_y > max_b && max_b > min_y) - || (max_b > max_y && max_y > min_b)) - return true; - else - return false; - } - - double x_diff = x1 - x2; - double y_diff = y1 - y2; - double a_diff = a2 - a1; - double b_diff = b2 - b1; - - double beta_denominator = b_diff - (y_diff / ((double)x_diff)) * a_diff; - - if (beta_denominator == 0) - { - // parallel lines - return false; - } - - double beta = (b2 - y2 - (y_diff / ((double)x_diff)) * (a2 - x2)) - / beta_denominator; - double alpha = (a2 - x2 - beta * (a_diff)) / x_diff; - - double upper_bound = 1 - epsilon; - double lower_bound = 0 + epsilon; - - return (beta < upper_bound && beta > lower_bound && alpha < upper_bound - && alpha > lower_bound); +// Overload of crosses from Boost.Geometry. +template +bool crosses(typename graph_traits::edge_descriptor e, + typename graph_traits::edge_descriptor f, + Graph const &g, + GridPositionMap const &drawing) +{ + using geometry::crosses; + using geometry::model::linestring; + using geometry::model::d2::point_xy; + using linestring2d = geometry::model::linestring>; + return crosses(make(e, g, drawing), + make(f, g, drawing)); } template < typename Graph, typename GridPositionMap, typename VertexIndexMap > @@ -161,33 +131,15 @@ bool is_straight_line_drawing( if (before != active_edges.end()) { - edge_t f = before->second; - vertex_t e_source(source(e, g)); - vertex_t e_target(target(e, g)); - vertex_t f_source(source(f, g)); - vertex_t f_target(target(f, g)); - - if (intersects(drawing[e_source].x, drawing[e_source].y, - drawing[e_target].x, drawing[e_target].y, - drawing[f_source].x, drawing[f_source].y, - drawing[f_target].x, drawing[f_target].y)) + if (crosses(e, f, g, drawing)) return false; } if (after != active_edges.end()) { - edge_t f = after->second; - vertex_t e_source(source(e, g)); - vertex_t e_target(target(e, g)); - vertex_t f_source(source(f, g)); - vertex_t f_target(target(f, g)); - - if (intersects(drawing[e_source].x, drawing[e_source].y, - drawing[e_target].x, drawing[e_target].y, - drawing[f_source].x, drawing[f_source].y, - drawing[f_target].x, drawing[f_target].y)) + if (crosses(e, f, g, drawing)) return false; } diff --git a/include/boost/graph/king_ordering.hpp b/include/boost/graph/king_ordering.hpp index 6a0bd939..9f441108 100644 --- a/include/boost/graph/king_ordering.hpp +++ b/include/boost/graph/king_ordering.hpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -49,8 +48,6 @@ namespace detail template < typename Vertex, typename Graph > void finish_vertex(Vertex, Graph& g) { - using namespace boost::placeholders; - typename graph_traits< Graph >::out_edge_iterator ei, ei_end; Vertex v, w; @@ -61,7 +58,7 @@ namespace detail reverse_iterator rbegin = Qptr->rbegin(); // heap the vertices already there - std::make_heap(rbegin, rend, boost::bind< bool >(comp, _2, _1)); + std::make_heap(rbegin, rend, [this](const auto& first, const auto& second) { return comp(second, first); }); unsigned i = 0; diff --git a/include/boost/graph/maximum_weighted_matching.hpp b/include/boost/graph/maximum_weighted_matching.hpp index 098a9c2a..86a133bd 100644 --- a/include/boost/graph/maximum_weighted_matching.hpp +++ b/include/boost/graph/maximum_weighted_matching.hpp @@ -1,5 +1,6 @@ //======================================================================= // Copyright (c) 2018 Yi Ji +// Copyright (c) 2025 Joris van Rantwijk // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -10,23 +11,35 @@ #ifndef BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP #define BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP -#include // for std::iter_swap -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include // for empty_matching + +#include +#include +#include +#include +#include +#include // for std::tie +#include // for std::pair, std::swap +#include namespace boost { -template < typename Graph, typename MateMap, typename VertexIndexMap > + +template typename property_traits< - typename property_map< Graph, edge_weight_t >::type >::value_type + typename property_map::type>::value_type matching_weight_sum(const Graph& g, MateMap mate, VertexIndexMap vm) { - typedef typename graph_traits< Graph >::vertex_iterator vertex_iterator_t; - typedef - typename graph_traits< Graph >::vertex_descriptor vertex_descriptor_t; - typedef typename property_traits< typename property_map< Graph, - edge_weight_t >::type >::value_type edge_property_t; + using vertex_iterator_t = typename graph_traits::vertex_iterator; + using vertex_descriptor_t = typename graph_traits::vertex_descriptor; + using edge_property_t = typename property_traits< + typename property_map::type>::value_type; edge_property_t weight_sum = 0; vertex_iterator_t vi, vi_end; @@ -34,1230 +47,65 @@ matching_weight_sum(const Graph& g, MateMap mate, VertexIndexMap vm) for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { vertex_descriptor_t v = *vi; - if (get(mate, v) != graph_traits< Graph >::null_vertex() + if (get(mate, v) != graph_traits::null_vertex() && get(vm, v) < get(vm, get(mate, v))) weight_sum += get(edge_weight, g, edge(v, mate[v], g).first); } return weight_sum; } -template < typename Graph, typename MateMap > +template inline typename property_traits< - typename property_map< Graph, edge_weight_t >::type >::value_type + typename property_map::type>::value_type matching_weight_sum(const Graph& g, MateMap mate) { return matching_weight_sum(g, mate, get(vertex_index, g)); } -template < typename Graph, typename MateMap, typename VertexIndexMap > -class weighted_augmenting_path_finder -{ -public: - template < typename T > struct map_vertex_to_ - { - typedef boost::iterator_property_map< - typename std::vector< T >::iterator, VertexIndexMap > - type; - }; - typedef typename graph::detail::VERTEX_STATE vertex_state_t; - typedef typename graph_traits< Graph >::vertex_iterator vertex_iterator_t; - typedef - typename graph_traits< Graph >::vertex_descriptor vertex_descriptor_t; - typedef typename std::vector< vertex_descriptor_t >::const_iterator - vertex_vec_iter_t; - typedef - typename graph_traits< Graph >::out_edge_iterator out_edge_iterator_t; - typedef typename graph_traits< Graph >::edge_descriptor edge_descriptor_t; - typedef typename graph_traits< Graph >::edge_iterator edge_iterator_t; - typedef typename property_traits< typename property_map< Graph, - edge_weight_t >::type >::value_type edge_property_t; - typedef std::deque< vertex_descriptor_t > vertex_list_t; - typedef std::vector< edge_descriptor_t > edge_list_t; - typedef typename map_vertex_to_< vertex_descriptor_t >::type - vertex_to_vertex_map_t; - typedef - typename map_vertex_to_< edge_property_t >::type vertex_to_weight_map_t; - typedef typename map_vertex_to_< bool >::type vertex_to_bool_map_t; - typedef typename map_vertex_to_< std::pair< vertex_descriptor_t, - vertex_descriptor_t > >::type vertex_to_pair_map_t; - typedef - typename map_vertex_to_< std::pair< edge_descriptor_t, bool > >::type - vertex_to_edge_map_t; - typedef typename map_vertex_to_< vertex_to_edge_map_t >::type - vertex_pair_to_edge_map_t; - - class blossom - { - public: - typedef boost::shared_ptr< blossom > blossom_ptr_t; - std::vector< blossom_ptr_t > sub_blossoms; - edge_property_t dual_var; - blossom_ptr_t father; - - blossom() : dual_var(0), father(blossom_ptr_t()) {} - - // get the base vertex of a blossom by recursively getting - // its base sub-blossom, which is always the first one in - // sub_blossoms because of how we create and maintain blossoms - virtual vertex_descriptor_t get_base() const - { - const blossom* b = this; - while (!b->sub_blossoms.empty()) - b = b->sub_blossoms[0].get(); - return b->get_base(); - } - - // set a sub-blossom as a blossom's base by exchanging it - // with its first sub-blossom - void set_base(const blossom_ptr_t& sub) - { - for (blossom_iterator_t bi = sub_blossoms.begin(); - bi != sub_blossoms.end(); ++bi) - { - if (sub.get() == bi->get()) - { - std::iter_swap(sub_blossoms.begin(), bi); - break; - } - } - } - - // get all vertices inside recursively - virtual std::vector< vertex_descriptor_t > vertices() const - { - std::vector< vertex_descriptor_t > all_vertices; - for (typename std::vector< blossom_ptr_t >::const_iterator bi - = sub_blossoms.begin(); - bi != sub_blossoms.end(); ++bi) - { - std::vector< vertex_descriptor_t > some_vertices - = (*bi)->vertices(); - all_vertices.insert(all_vertices.end(), some_vertices.begin(), - some_vertices.end()); - } - return all_vertices; - } - }; - - // a trivial_blossom only has one vertex and no sub-blossom; - // for each vertex v, in_blossom[v] is the trivial_blossom that contains it - // directly - class trivial_blossom : public blossom - { - public: - trivial_blossom(vertex_descriptor_t v) : trivial_vertex(v) {} - virtual vertex_descriptor_t get_base() const { return trivial_vertex; } - - virtual std::vector< vertex_descriptor_t > vertices() const - { - std::vector< vertex_descriptor_t > all_vertices; - all_vertices.push_back(trivial_vertex); - return all_vertices; - } - - private: - vertex_descriptor_t trivial_vertex; - }; - - typedef boost::shared_ptr< blossom > blossom_ptr_t; - typedef typename std::vector< blossom_ptr_t >::iterator blossom_iterator_t; - typedef - typename map_vertex_to_< blossom_ptr_t >::type vertex_to_blossom_map_t; - - weighted_augmenting_path_finder( - const Graph& arg_g, MateMap arg_mate, VertexIndexMap arg_vm) - : g(arg_g) - , vm(arg_vm) - , null_edge(std::pair< edge_descriptor_t, bool >( - num_edges(g) == 0 ? edge_descriptor_t() : *edges(g).first, false)) - , mate_vector(num_vertices(g)) - , label_S_vector(num_vertices(g), graph_traits< Graph >::null_vertex()) - , label_T_vector(num_vertices(g), graph_traits< Graph >::null_vertex()) - , outlet_vector(num_vertices(g), graph_traits< Graph >::null_vertex()) - , tau_idx_vector(num_vertices(g), graph_traits< Graph >::null_vertex()) - , dual_var_vector(std::vector< edge_property_t >( - num_vertices(g), std::numeric_limits< edge_property_t >::min())) - , pi_vector(std::vector< edge_property_t >( - num_vertices(g), std::numeric_limits< edge_property_t >::max())) - , gamma_vector(std::vector< edge_property_t >( - num_vertices(g), std::numeric_limits< edge_property_t >::max())) - , tau_vector(std::vector< edge_property_t >( - num_vertices(g), std::numeric_limits< edge_property_t >::max())) - , in_blossom_vector(num_vertices(g)) - , old_label_vector(num_vertices(g)) - , critical_edge_vectors(num_vertices(g), - std::vector< std::pair< edge_descriptor_t, bool > >( - num_vertices(g), null_edge)) - , - - mate(mate_vector.begin(), vm) - , label_S(label_S_vector.begin(), vm) - , label_T(label_T_vector.begin(), vm) - , outlet(outlet_vector.begin(), vm) - , tau_idx(tau_idx_vector.begin(), vm) - , dual_var(dual_var_vector.begin(), vm) - , pi(pi_vector.begin(), vm) - , gamma(gamma_vector.begin(), vm) - , tau(tau_vector.begin(), vm) - , in_blossom(in_blossom_vector.begin(), vm) - , old_label(old_label_vector.begin(), vm) - { - vertex_iterator_t vi, vi_end; - edge_iterator_t ei, ei_end; - - edge_property_t max_weight - = std::numeric_limits< edge_property_t >::min(); - for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) - max_weight = std::max(max_weight, get(edge_weight, g, *ei)); - - typename std::vector< - std::vector< std::pair< edge_descriptor_t, bool > > >::iterator vei; - - for (boost::tie(vi, vi_end) = vertices(g), - vei = critical_edge_vectors.begin(); - vi != vi_end; ++vi, ++vei) - { - vertex_descriptor_t u = *vi; - mate[u] = get(arg_mate, u); - dual_var[u] = 2 * max_weight; - in_blossom[u] = boost::make_shared< trivial_blossom >(u); - outlet[u] = u; - critical_edge_vector.push_back( - vertex_to_edge_map_t(vei->begin(), vm)); - } - - critical_edge - = vertex_pair_to_edge_map_t(critical_edge_vector.begin(), vm); - - init(); - } - - // return the top blossom where v is contained inside - blossom_ptr_t in_top_blossom(vertex_descriptor_t v) const - { - blossom_ptr_t b = in_blossom[v]; - while (b->father != blossom_ptr_t()) - b = b->father; - return b; - } - - // check if vertex v is in blossom b - bool is_in_blossom(blossom_ptr_t b, vertex_descriptor_t v) const - { - if (v == graph_traits< Graph >::null_vertex()) - return false; - blossom_ptr_t vb = in_blossom[v]->father; - while (vb != blossom_ptr_t()) - { - if (vb.get() == b.get()) - return true; - vb = vb->father; - } - return false; - } - - // return the base vertex of the top blossom that contains v - inline vertex_descriptor_t base_vertex(vertex_descriptor_t v) const - { - return in_top_blossom(v)->get_base(); - } - - // add an existed top blossom of base vertex v into new top - // blossom b as its sub-blossom - void add_sub_blossom(blossom_ptr_t b, vertex_descriptor_t v) - { - blossom_ptr_t sub = in_top_blossom(v); - sub->father = b; - b->sub_blossoms.push_back(sub); - if (sub->sub_blossoms.empty()) - return; - for (blossom_iterator_t bi = top_blossoms.begin(); - bi != top_blossoms.end(); ++bi) - { - if (bi->get() == sub.get()) - { - top_blossoms.erase(bi); - break; - } - } - } - - // when a top blossom is created or its base vertex getting an S-label, - // add all edges incident to this blossom into even_edges - void bloom(blossom_ptr_t b) - { - std::vector< vertex_descriptor_t > vertices_of_b = b->vertices(); - vertex_vec_iter_t vi; - for (vi = vertices_of_b.begin(); vi != vertices_of_b.end(); ++vi) - { - out_edge_iterator_t oei, oei_end; - for (boost::tie(oei, oei_end) = out_edges(*vi, g); oei != oei_end; - ++oei) - { - if (target(*oei, g) != *vi && mate[*vi] != target(*oei, g)) - even_edges.push_back(*oei); - } - } - } - - // assigning a T-label to a non S-vertex, along with outlet and updating pi - // value if updated pi[v] equals zero, augment the matching from its mate - // vertex - void put_T_label(vertex_descriptor_t v, vertex_descriptor_t T_label, - vertex_descriptor_t outlet_v, edge_property_t pi_v) - { - if (label_S[v] != graph_traits< Graph >::null_vertex()) - return; - - label_T[v] = T_label; - outlet[v] = outlet_v; - pi[v] = pi_v; - - vertex_descriptor_t v_mate = mate[v]; - if (pi[v] == 0) - { - label_T[v_mate] = graph_traits< Graph >::null_vertex(); - label_S[v_mate] = v; - bloom(in_top_blossom(v_mate)); - } - } - - // get the missing T-label for a to-be-expanded base vertex - // the missing T-label is the last vertex of the path from outlet[v] to v - std::pair< vertex_descriptor_t, vertex_descriptor_t > missing_label( - vertex_descriptor_t b_base) - { - vertex_descriptor_t missing_outlet = outlet[b_base]; - - if (outlet[b_base] == b_base) - return std::make_pair( - graph_traits< Graph >::null_vertex(), missing_outlet); - - vertex_iterator_t vi, vi_end; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - old_label[*vi] = std::make_pair(label_T[*vi], outlet[*vi]); - - std::pair< vertex_descriptor_t, vertex_state_t > child( - outlet[b_base], graph::detail::V_EVEN); - blossom_ptr_t b = in_blossom[child.first]; - for (; b->father->father != blossom_ptr_t(); b = b->father) - ; - child.first = b->get_base(); - - if (child.first == b_base) - return std::make_pair( - graph_traits< Graph >::null_vertex(), missing_outlet); - - while (true) - { - std::pair< vertex_descriptor_t, vertex_state_t > child_parent - = parent(child, true); - - for (b = in_blossom[child_parent.first]; - b->father->father != blossom_ptr_t(); b = b->father) - ; - missing_outlet = child_parent.first; - child_parent.first = b->get_base(); - - if (child_parent.first == b_base) - break; - else - child = child_parent; - } - return std::make_pair(child.first, missing_outlet); - } - - // expand a top blossom, put all its non-trivial sub-blossoms into - // top_blossoms - blossom_iterator_t expand_blossom( - blossom_iterator_t bi, std::vector< blossom_ptr_t >& new_ones) - { - blossom_ptr_t b = *bi; - for (blossom_iterator_t i = b->sub_blossoms.begin(); - i != b->sub_blossoms.end(); ++i) - { - blossom_ptr_t sub_blossom = *i; - vertex_descriptor_t sub_base = sub_blossom->get_base(); - label_S[sub_base] = label_T[sub_base] - = graph_traits< Graph >::null_vertex(); - outlet[sub_base] = sub_base; - sub_blossom->father = blossom_ptr_t(); - // new top blossoms cannot be pushed back into top_blossoms - // immediately, because push_back() may cause reallocation and then - // invalid iterators - if (!sub_blossom->sub_blossoms.empty()) - new_ones.push_back(sub_blossom); - } - return top_blossoms.erase(bi); - } - - // when expanding a T-blossom with base v, it requires more operations: - // supply the missing T-labels for new base vertices by picking the minimum - // tau from vertices of each corresponding new top-blossoms; when label_T[v] - // is null or we have a smaller tau from missing_label(v), replace T-label - // and outlet of v (but don't bloom v) - blossom_iterator_t expand_T_blossom( - blossom_iterator_t bi, std::vector< blossom_ptr_t >& new_ones) - { - blossom_ptr_t b = *bi; - - vertex_descriptor_t b_base = b->get_base(); - std::pair< vertex_descriptor_t, vertex_descriptor_t > T_and_outlet - = missing_label(b_base); - - blossom_iterator_t next_bi = expand_blossom(bi, new_ones); - - for (blossom_iterator_t i = b->sub_blossoms.begin(); - i != b->sub_blossoms.end(); ++i) - { - blossom_ptr_t sub_blossom = *i; - vertex_descriptor_t sub_base = sub_blossom->get_base(); - vertex_descriptor_t min_tau_v - = graph_traits< Graph >::null_vertex(); - edge_property_t min_tau - = std::numeric_limits< edge_property_t >::max(); - - std::vector< vertex_descriptor_t > sub_vertices - = sub_blossom->vertices(); - for (vertex_vec_iter_t v = sub_vertices.begin(); - v != sub_vertices.end(); ++v) - { - if (tau[*v] < min_tau) - { - min_tau = tau[*v]; - min_tau_v = *v; - } - } - - if (min_tau < std::numeric_limits< edge_property_t >::max()) - put_T_label( - sub_base, tau_idx[min_tau_v], min_tau_v, tau[min_tau_v]); - } - - if (label_T[b_base] == graph_traits< Graph >::null_vertex() - || tau[old_label[b_base].second] < pi[b_base]) - boost::tie(label_T[b_base], outlet[b_base]) = T_and_outlet; - - return next_bi; - } - - // when vertices v and w are matched to each other by augmenting, - // we must set v/w as base vertex of any blossom who contains v/w and - // is a sub-blossom of their lowest (smallest) common blossom - void adjust_blossom(vertex_descriptor_t v, vertex_descriptor_t w) - { - blossom_ptr_t vb = in_blossom[v], wb = in_blossom[w], - lowest_common_blossom; - std::vector< blossom_ptr_t > v_ancestors, w_ancestors; - - while (vb->father != blossom_ptr_t()) - { - v_ancestors.push_back(vb->father); - vb = vb->father; - } - while (wb->father != blossom_ptr_t()) - { - w_ancestors.push_back(wb->father); - wb = wb->father; - } - - typename std::vector< blossom_ptr_t >::reverse_iterator i, j; - i = v_ancestors.rbegin(); - j = w_ancestors.rbegin(); - while (i != v_ancestors.rend() && j != w_ancestors.rend() - && i->get() == j->get()) - { - lowest_common_blossom = *i; - ++i; - ++j; - } - - vb = in_blossom[v]; - wb = in_blossom[w]; - while (vb->father != lowest_common_blossom) - { - vb->father->set_base(vb); - vb = vb->father; - } - while (wb->father != lowest_common_blossom) - { - wb->father->set_base(wb); - wb = wb->father; - } - } - - // every edge weight is multiplied by 4 to ensure integer weights - // throughout the algorithm if all input weights are integers - inline edge_property_t slack(const edge_descriptor_t& e) const - { - vertex_descriptor_t v, w; - v = source(e, g); - w = target(e, g); - return dual_var[v] + dual_var[w] - 4 * get(edge_weight, g, e); - } - - // backtrace one step on vertex v along the augmenting path - // by its labels and its vertex state; - // boolean parameter "use_old" means whether we are updating labels, - // if we are, then we use old labels to backtrace and also we - // don't jump to its base vertex when we reach an odd vertex - std::pair< vertex_descriptor_t, vertex_state_t > parent( - std::pair< vertex_descriptor_t, vertex_state_t > v, - bool use_old = false) const - { - if (v.second == graph::detail::V_EVEN) - { - // a paranoid check: label_S shoule be the same as mate in - // backtracing - if (label_S[v.first] == graph_traits< Graph >::null_vertex()) - label_S[v.first] = mate[v.first]; - return std::make_pair(label_S[v.first], graph::detail::V_ODD); - } - else if (v.second == graph::detail::V_ODD) - { - vertex_descriptor_t w = use_old ? old_label[v.first].first - : base_vertex(label_T[v.first]); - return std::make_pair(w, graph::detail::V_EVEN); - } - return std::make_pair(v.first, graph::detail::V_UNREACHED); - } - - // backtrace from vertices v and w to their free (unmatched) ancesters, - // return the nearest common ancestor (null_vertex if none) of v and w - vertex_descriptor_t nearest_common_ancestor(vertex_descriptor_t v, - vertex_descriptor_t w, vertex_descriptor_t& v_free_ancestor, - vertex_descriptor_t& w_free_ancestor) const - { - std::pair< vertex_descriptor_t, vertex_state_t > v_up( - v, graph::detail::V_EVEN); - std::pair< vertex_descriptor_t, vertex_state_t > w_up( - w, graph::detail::V_EVEN); - vertex_descriptor_t nca; - nca = w_free_ancestor = v_free_ancestor - = graph_traits< Graph >::null_vertex(); - - std::vector< bool > ancestor_of_w_vector(num_vertices(g), false); - std::vector< bool > ancestor_of_v_vector(num_vertices(g), false); - vertex_to_bool_map_t ancestor_of_w(ancestor_of_w_vector.begin(), vm); - vertex_to_bool_map_t ancestor_of_v(ancestor_of_v_vector.begin(), vm); - - while (nca == graph_traits< Graph >::null_vertex() - && (v_free_ancestor == graph_traits< Graph >::null_vertex() - || w_free_ancestor == graph_traits< Graph >::null_vertex())) - { - ancestor_of_w[w_up.first] = true; - ancestor_of_v[v_up.first] = true; - - if (w_free_ancestor == graph_traits< Graph >::null_vertex()) - w_up = parent(w_up); - if (v_free_ancestor == graph_traits< Graph >::null_vertex()) - v_up = parent(v_up); - - if (mate[v_up.first] == graph_traits< Graph >::null_vertex()) - v_free_ancestor = v_up.first; - if (mate[w_up.first] == graph_traits< Graph >::null_vertex()) - w_free_ancestor = w_up.first; - - if (ancestor_of_w[v_up.first] == true || v_up.first == w_up.first) - nca = v_up.first; - else if (ancestor_of_v[w_up.first] == true) - nca = w_up.first; - else if (v_free_ancestor == w_free_ancestor - && v_free_ancestor != graph_traits< Graph >::null_vertex()) - nca = v_up.first; - } - - return nca; - } - - // when a new top blossom b is created by connecting (v, w), we add - // sub-blossoms into b along backtracing from v_prime and w_prime to - // stop_vertex (the base vertex); also, we set labels and outlet for each - // base vertex we pass by - void make_blossom(blossom_ptr_t b, vertex_descriptor_t w_prime, - vertex_descriptor_t v_prime, vertex_descriptor_t stop_vertex) - { - std::pair< vertex_descriptor_t, vertex_state_t > u( - v_prime, graph::detail::V_ODD); - std::pair< vertex_descriptor_t, vertex_state_t > u_up( - w_prime, graph::detail::V_EVEN); - - for (; u_up.first != stop_vertex; u = u_up, u_up = parent(u)) - { - if (u_up.second == graph::detail::V_EVEN) - { - if (!in_top_blossom(u_up.first)->sub_blossoms.empty()) - outlet[u_up.first] = label_T[u.first]; - label_T[u_up.first] = outlet[u.first]; - } - else if (u_up.second == graph::detail::V_ODD) - label_S[u_up.first] = u.first; - - add_sub_blossom(b, u_up.first); - } - } - - // the design of recursively expanding augmenting path in - // (reversed_)retrieve_augmenting_path functions is inspired by same - // functions in max_cardinality_matching.hpp; except that in weighted - // matching, we use "outlet" vertices instead of "bridge" vertex pairs: if - // blossom b is the smallest non-trivial blossom that contains its base - // vertex v, then v and outlet[v] are where augmenting path enters and - // leaves b - void retrieve_augmenting_path( - vertex_descriptor_t v, vertex_descriptor_t w, vertex_state_t v_state) - { - if (v == w) - aug_path.push_back(v); - else if (v_state == graph::detail::V_EVEN) - { - aug_path.push_back(v); - retrieve_augmenting_path(label_S[v], w, graph::detail::V_ODD); - } - else if (v_state == graph::detail::V_ODD) - { - if (outlet[v] == v) - aug_path.push_back(v); - else - reversed_retrieve_augmenting_path( - outlet[v], v, graph::detail::V_EVEN); - retrieve_augmenting_path(label_T[v], w, graph::detail::V_EVEN); - } - } - - void reversed_retrieve_augmenting_path( - vertex_descriptor_t v, vertex_descriptor_t w, vertex_state_t v_state) - { - if (v == w) - aug_path.push_back(v); - else if (v_state == graph::detail::V_EVEN) - { - reversed_retrieve_augmenting_path( - label_S[v], w, graph::detail::V_ODD); - aug_path.push_back(v); - } - else if (v_state == graph::detail::V_ODD) - { - reversed_retrieve_augmenting_path( - label_T[v], w, graph::detail::V_EVEN); - if (outlet[v] != v) - retrieve_augmenting_path(outlet[v], v, graph::detail::V_EVEN); - else - aug_path.push_back(v); - } - } - - // correct labels for vertices in the augmenting path - void relabel(vertex_descriptor_t v) - { - blossom_ptr_t b = in_blossom[v]->father; - - if (!is_in_blossom(b, mate[v])) - { // if v is a new base vertex - std::pair< vertex_descriptor_t, vertex_state_t > u( - v, graph::detail::V_EVEN); - while (label_S[u.first] != u.first - && is_in_blossom(b, label_S[u.first])) - u = parent(u, true); - - vertex_descriptor_t old_base = u.first; - if (label_S[old_base] != old_base) - { // if old base is not exposed - label_T[v] = label_S[old_base]; - outlet[v] = old_base; - } - else - { // if old base is exposed then new label_T[v] is not in b, - // we must (i) make b2 the smallest blossom containing v but not - // as base vertex (ii) backtrace from b2's new base vertex to b - label_T[v] = graph_traits< Graph >::null_vertex(); - for (b = b->father; b != blossom_ptr_t() && b->get_base() == v; - b = b->father) - ; - if (b != blossom_ptr_t()) - { - u = std::make_pair(b->get_base(), graph::detail::V_ODD); - while (!is_in_blossom( - in_blossom[v]->father, old_label[u.first].first)) - u = parent(u, true); - label_T[v] = u.first; - outlet[v] = old_label[u.first].first; - } - } - } - else if (label_S[v] == v || !is_in_blossom(b, label_S[v])) - { // if v is an old base vertex - // let u be the new base vertex; backtrace from u's old T-label - std::pair< vertex_descriptor_t, vertex_state_t > u( - b->get_base(), graph::detail::V_ODD); - while ( - old_label[u.first].first != graph_traits< Graph >::null_vertex() - && old_label[u.first].first != v) - u = parent(u, true); - label_T[v] = old_label[u.first].second; - outlet[v] = v; - } - else // if v is neither a new nor an old base vertex - label_T[v] = label_S[v]; - } - - void augmenting(vertex_descriptor_t v, vertex_descriptor_t v_free_ancestor, - vertex_descriptor_t w, vertex_descriptor_t w_free_ancestor) - { - vertex_iterator_t vi, vi_end; - - // retrieve the augmenting path and put it in aug_path - reversed_retrieve_augmenting_path( - v, v_free_ancestor, graph::detail::V_EVEN); - retrieve_augmenting_path(w, w_free_ancestor, graph::detail::V_EVEN); - - // augment the matching along aug_path - vertex_descriptor_t a, b; - vertex_list_t reversed_aug_path; - while (!aug_path.empty()) - { - a = aug_path.front(); - aug_path.pop_front(); - reversed_aug_path.push_back(a); - b = aug_path.front(); - aug_path.pop_front(); - reversed_aug_path.push_back(b); - - mate[a] = b; - mate[b] = a; - - // reset base vertex for every blossom in augment path - adjust_blossom(a, b); - } - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - old_label[*vi] = std::make_pair(label_T[*vi], outlet[*vi]); - - // correct labels for in-blossom vertices along aug_path - while (!reversed_aug_path.empty()) - { - a = reversed_aug_path.front(); - reversed_aug_path.pop_front(); - - if (in_blossom[a]->father != blossom_ptr_t()) - relabel(a); - } - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - vertex_descriptor_t u = *vi; - if (mate[u] != graph_traits< Graph >::null_vertex()) - label_S[u] = mate[u]; - } - - // expand blossoms with zero dual variables - std::vector< blossom_ptr_t > new_top_blossoms; - for (blossom_iterator_t bi = top_blossoms.begin(); - bi != top_blossoms.end();) - { - if ((*bi)->dual_var <= 0) - bi = expand_blossom(bi, new_top_blossoms); - else - ++bi; - } - top_blossoms.insert(top_blossoms.end(), new_top_blossoms.begin(), - new_top_blossoms.end()); - init(); - } - - // create a new blossom and set labels for vertices inside - void blossoming(vertex_descriptor_t v, vertex_descriptor_t v_prime, - vertex_descriptor_t w, vertex_descriptor_t w_prime, - vertex_descriptor_t nca) - { - vertex_iterator_t vi, vi_end; - - std::vector< bool > is_old_base_vector(num_vertices(g)); - vertex_to_bool_map_t is_old_base(is_old_base_vector.begin(), vm); - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - if (*vi == base_vertex(*vi)) - is_old_base[*vi] = true; - } - - blossom_ptr_t b = boost::make_shared< blossom >(); - add_sub_blossom(b, nca); - - label_T[w_prime] = v; - label_T[v_prime] = w; - outlet[w_prime] = w; - outlet[v_prime] = v; - - make_blossom(b, w_prime, v_prime, nca); - make_blossom(b, v_prime, w_prime, nca); - - label_T[nca] = graph_traits< Graph >::null_vertex(); - outlet[nca] = nca; - - top_blossoms.push_back(b); - bloom(b); - - // set gamma[b_base] = min_slack{critical_edge(b_base, other_base)} - // where each critical edge is updated before, by - // argmin{slack(old_bases_in_b, other_base)}; - vertex_vec_iter_t i, j; - std::vector< vertex_descriptor_t > b_vertices = b->vertices(), - old_base_in_b, other_base; - vertex_descriptor_t b_base = b->get_base(); - for (i = b_vertices.begin(); i != b_vertices.end(); ++i) - { - if (is_old_base[*i]) - old_base_in_b.push_back(*i); - } - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - if (*vi != b_base && *vi == base_vertex(*vi)) - other_base.push_back(*vi); - } - for (i = other_base.begin(); i != other_base.end(); ++i) - { - edge_property_t min_slack - = std::numeric_limits< edge_property_t >::max(); - std::pair< edge_descriptor_t, bool > b_vi = null_edge; - for (j = old_base_in_b.begin(); j != old_base_in_b.end(); ++j) - { - if (critical_edge[*j][*i] != null_edge - && min_slack > slack(critical_edge[*j][*i].first)) - { - min_slack = slack(critical_edge[*j][*i].first); - b_vi = critical_edge[*j][*i]; - } - } - critical_edge[b_base][*i] = critical_edge[*i][b_base] = b_vi; - } - gamma[b_base] = std::numeric_limits< edge_property_t >::max(); - for (i = other_base.begin(); i != other_base.end(); ++i) - { - if (critical_edge[b_base][*i] != null_edge) - gamma[b_base] = std::min( - gamma[b_base], slack(critical_edge[b_base][*i].first)); - } - } - - void init() - { - even_edges.clear(); - - vertex_iterator_t vi, vi_end; - typename std::vector< - std::vector< std::pair< edge_descriptor_t, bool > > >::iterator vei; - - for (boost::tie(vi, vi_end) = vertices(g), - vei = critical_edge_vectors.begin(); - vi != vi_end; ++vi, ++vei) - { - vertex_descriptor_t u = *vi; - out_edge_iterator_t ei, ei_end; - - gamma[u] = tau[u] = pi[u] - = std::numeric_limits< edge_property_t >::max(); - std::fill(vei->begin(), vei->end(), null_edge); - - if (base_vertex(u) != u) - continue; - - label_S[u] = label_T[u] = graph_traits< Graph >::null_vertex(); - outlet[u] = u; - - if (mate[u] == graph_traits< Graph >::null_vertex()) - { - label_S[u] = u; - bloom(in_top_blossom(u)); - } - } - } - - bool augment_matching() - { - vertex_descriptor_t v, w, w_free_ancestor, v_free_ancestor; - v = w = w_free_ancestor = v_free_ancestor - = graph_traits< Graph >::null_vertex(); - bool found_alternating_path = false; - - // note that we only use edges of zero slack value for augmenting - while (!even_edges.empty() && !found_alternating_path) - { - // search for augmenting paths depth-first - edge_descriptor_t current_edge = even_edges.back(); - even_edges.pop_back(); - - v = source(current_edge, g); - w = target(current_edge, g); - - vertex_descriptor_t v_prime = base_vertex(v); - vertex_descriptor_t w_prime = base_vertex(w); - - // w_prime == v_prime implies that we get an edge that has been - // shrunk into a blossom - if (v_prime == w_prime) - continue; - - // a paranoid check - if (label_S[v_prime] == graph_traits< Graph >::null_vertex()) - { - std::swap(v_prime, w_prime); - std::swap(v, w); - } - - // w_prime may be unlabeled or have a T-label; replace the existed - // T-label if the edge slack is smaller than current pi[w_prime] and - // update it. Note that a T-label is "deserved" only when pi equals - // zero. also update tau and tau_idx so that tau_idx becomes T-label - // when a T-blossom is expanded - if (label_S[w_prime] == graph_traits< Graph >::null_vertex()) - { - if (slack(current_edge) < pi[w_prime]) - put_T_label(w_prime, v, w, slack(current_edge)); - if (slack(current_edge) < tau[w]) - { - if (in_blossom[w]->father == blossom_ptr_t() - || label_T[w_prime] == v - || label_T[w_prime] - == graph_traits< Graph >::null_vertex() - || nearest_common_ancestor(v_prime, label_T[w_prime], - v_free_ancestor, w_free_ancestor) - == graph_traits< Graph >::null_vertex()) - { - tau[w] = slack(current_edge); - tau_idx[w] = v; - } - } - } - - else - { - if (slack(current_edge) > 0) - { - // update gamma and critical_edges when we have a smaller - // edge slack - gamma[v_prime] - = std::min(gamma[v_prime], slack(current_edge)); - gamma[w_prime] - = std::min(gamma[w_prime], slack(current_edge)); - if (critical_edge[v_prime][w_prime] == null_edge - || slack(critical_edge[v_prime][w_prime].first) - > slack(current_edge)) - { - critical_edge[v_prime][w_prime] - = std::pair< edge_descriptor_t, bool >( - current_edge, true); - critical_edge[w_prime][v_prime] - = std::pair< edge_descriptor_t, bool >( - current_edge, true); - } - continue; - } - else if (slack(current_edge) == 0) - { - // if nca is null_vertex then we have an augmenting path; - // otherwise we have a new top blossom with nca as its base - // vertex - vertex_descriptor_t nca = nearest_common_ancestor( - v_prime, w_prime, v_free_ancestor, w_free_ancestor); - - if (nca == graph_traits< Graph >::null_vertex()) - found_alternating_path - = true; // to break out of the loop - else - blossoming(v, v_prime, w, w_prime, nca); - } - } - } - - if (!found_alternating_path) - return false; - - augmenting(v, v_free_ancestor, w, w_free_ancestor); - return true; - } - - // slack the vertex and blossom dual variables when there is no augmenting - // path found according to the primal-dual method - bool adjust_dual() - { - edge_property_t delta1, delta2, delta3, delta4, delta; - delta1 = delta2 = delta3 = delta4 - = std::numeric_limits< edge_property_t >::max(); - - vertex_iterator_t vi, vi_end; - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - delta1 = std::min(delta1, dual_var[*vi]); - delta4 = pi[*vi] > 0 ? std::min(delta4, pi[*vi]) : delta4; - if (*vi == base_vertex(*vi)) - delta3 = std::min(delta3, gamma[*vi] / 2); - } - - for (blossom_iterator_t bi = top_blossoms.begin(); - bi != top_blossoms.end(); ++bi) - { - vertex_descriptor_t b_base = (*bi)->get_base(); - if (label_T[b_base] != graph_traits< Graph >::null_vertex() - && pi[b_base] == 0) - delta2 = std::min(delta2, (*bi)->dual_var / 2); - } - - delta = std::min(std::min(delta1, delta2), std::min(delta3, delta4)); - - // start updating dual variables, note that the order is important - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - vertex_descriptor_t v = *vi, v_prime = base_vertex(v); - - if (label_S[v_prime] != graph_traits< Graph >::null_vertex()) - dual_var[v] -= delta; - else if (label_T[v_prime] != graph_traits< Graph >::null_vertex() - && pi[v_prime] == 0) - dual_var[v] += delta; - - if (v == v_prime) - gamma[v] -= 2 * delta; - } - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - vertex_descriptor_t v_prime = base_vertex(*vi); - if (pi[v_prime] > 0) - tau[*vi] -= delta; - } - - for (blossom_iterator_t bi = top_blossoms.begin(); - bi != top_blossoms.end(); ++bi) - { - vertex_descriptor_t b_base = (*bi)->get_base(); - if (label_T[b_base] != graph_traits< Graph >::null_vertex() - && pi[b_base] == 0) - (*bi)->dual_var -= 2 * delta; - if (label_S[b_base] != graph_traits< Graph >::null_vertex()) - (*bi)->dual_var += 2 * delta; - } - - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - vertex_descriptor_t v = *vi; - if (pi[v] > 0) - pi[v] -= delta; - - // when some T-vertices have zero pi value, bloom their mates so - // that matching can be further augmented - if (label_T[v] != graph_traits< Graph >::null_vertex() - && pi[v] == 0) - put_T_label(v, label_T[v], outlet[v], pi[v]); - } - - // optimal solution reached, halt - if (delta == delta1) - return false; - - // expand odd blossoms with zero dual variables and zero pi value of - // their base vertices - if (delta == delta2 && delta != delta3) - { - std::vector< blossom_ptr_t > new_top_blossoms; - for (blossom_iterator_t bi = top_blossoms.begin(); - bi != top_blossoms.end();) - { - const blossom_ptr_t b = *bi; - vertex_descriptor_t b_base = b->get_base(); - if (b->dual_var == 0 - && label_T[b_base] != graph_traits< Graph >::null_vertex() - && pi[b_base] == 0) - bi = expand_T_blossom(bi, new_top_blossoms); - else - ++bi; - } - top_blossoms.insert(top_blossoms.end(), new_top_blossoms.begin(), - new_top_blossoms.end()); - } - - while (true) - { - // find a zero-slack critical edge (v, w) of zero gamma values - std::pair< edge_descriptor_t, bool > best_edge = null_edge; - std::vector< vertex_descriptor_t > base_nodes; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - { - if (*vi == base_vertex(*vi)) - base_nodes.push_back(*vi); - } - for (vertex_vec_iter_t i = base_nodes.begin(); - i != base_nodes.end(); ++i) - { - if (gamma[*i] == 0) - { - for (vertex_vec_iter_t j = base_nodes.begin(); - j != base_nodes.end(); ++j) - { - if (critical_edge[*i][*j] != null_edge - && slack(critical_edge[*i][*j].first) == 0) - best_edge = critical_edge[*i][*j]; - } - } - } - - // if not found, continue finding other augment matching - if (best_edge == null_edge) - { - bool augmented = augment_matching(); - return augmented || delta != delta1; - } - // if found, determine either augmenting or blossoming - vertex_descriptor_t v = source(best_edge.first, g), - w = target(best_edge.first, g); - vertex_descriptor_t v_prime = base_vertex(v), - w_prime = base_vertex(w), v_free_ancestor, - w_free_ancestor; - vertex_descriptor_t nca = nearest_common_ancestor( - v_prime, w_prime, v_free_ancestor, w_free_ancestor); - if (nca == graph_traits< Graph >::null_vertex()) - { - augmenting(v, v_free_ancestor, w, w_free_ancestor); - return true; - } - else - blossoming(v, v_prime, w, w_prime, nca); - } - - return false; - } - - template < typename PropertyMap > void get_current_matching(PropertyMap pm) - { - vertex_iterator_t vi, vi_end; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - put(pm, *vi, mate[*vi]); - } - -private: - const Graph& g; - VertexIndexMap vm; - const std::pair< edge_descriptor_t, bool > null_edge; - - // storage for the property maps below - std::vector< vertex_descriptor_t > mate_vector; - std::vector< vertex_descriptor_t > label_S_vector, label_T_vector; - std::vector< vertex_descriptor_t > outlet_vector; - std::vector< vertex_descriptor_t > tau_idx_vector; - std::vector< edge_property_t > dual_var_vector; - std::vector< edge_property_t > pi_vector, gamma_vector, tau_vector; - std::vector< blossom_ptr_t > in_blossom_vector; - std::vector< std::pair< vertex_descriptor_t, vertex_descriptor_t > > - old_label_vector; - std::vector< vertex_to_edge_map_t > critical_edge_vector; - std::vector< std::vector< std::pair< edge_descriptor_t, bool > > > - critical_edge_vectors; - - // iterator property maps - vertex_to_vertex_map_t mate; - vertex_to_vertex_map_t label_S; // v has an S-label -> v can be an even - // vertex, label_S[v] is its mate - vertex_to_vertex_map_t - label_T; // v has a T-label -> v can be an odd vertex, label_T[v] is its - // predecessor in aug_path - vertex_to_vertex_map_t outlet; - vertex_to_vertex_map_t tau_idx; - vertex_to_weight_map_t dual_var; - vertex_to_weight_map_t pi, gamma, tau; - vertex_to_blossom_map_t - in_blossom; // map any vertex v to the trivial blossom containing v - vertex_to_pair_map_t old_label; // before - // relabeling or expanding T-blossoms - vertex_pair_to_edge_map_t - critical_edge; // an not matched edge (v, w) is critical if v and w - // belongs to different S-blossoms - - vertex_list_t aug_path; - edge_list_t even_edges; - std::vector< blossom_ptr_t > top_blossoms; -}; - -template < typename Graph, typename MateMap, typename VertexIndexMap > -void maximum_weighted_matching(const Graph& g, MateMap mate, VertexIndexMap vm) -{ - empty_matching< Graph, MateMap >::find_matching(g, mate); - weighted_augmenting_path_finder< Graph, MateMap, VertexIndexMap > augmentor( - g, mate, vm); - - // can have |V| times augmenting at most - for (std::size_t t = 0; t < num_vertices(g); ++t) - { - bool augmented = false; - while (!augmented) - { - augmented = augmentor.augment_matching(); - if (!augmented) - { - // halt if adjusting dual variables can't bring potential - // augment - if (!augmentor.adjust_dual()) - break; - } - } - if (!augmented) - break; - } - - augmentor.get_current_matching(mate); -} - -template < typename Graph, typename MateMap > -inline void maximum_weighted_matching(const Graph& g, MateMap mate) -{ - maximum_weighted_matching(g, mate, get(vertex_index, g)); -} - // brute-force matcher searches all possible combinations of matched edges to // get the maximum weighted matching which can be used for testing on small // graphs (within dozens vertices) -template < typename Graph, typename MateMap, typename VertexIndexMap > +template class brute_force_matching { public: - typedef - typename graph_traits< Graph >::vertex_descriptor vertex_descriptor_t; - typedef typename graph_traits< Graph >::vertex_iterator vertex_iterator_t; - typedef - typename std::vector< vertex_descriptor_t >::iterator vertex_vec_iter_t; - typedef typename graph_traits< Graph >::edge_iterator edge_iterator_t; - typedef boost::iterator_property_map< vertex_vec_iter_t, VertexIndexMap > - vertex_to_vertex_map_t; + using vertex_descriptor_t = typename graph_traits::vertex_descriptor; + using vertex_iterator_t = typename graph_traits::vertex_iterator; + using vertex_vec_iter_t = + typename std::vector::iterator; + using edge_iterator_t = typename graph_traits::edge_iterator; + using vertex_to_vertex_map_t = + boost::iterator_property_map; brute_force_matching( const Graph& arg_g, MateMap arg_mate, VertexIndexMap arg_vm) - : g(arg_g) + : g(&arg_g) , vm(arg_vm) - , mate_vector(num_vertices(g)) - , best_mate_vector(num_vertices(g)) + , mate_vector(num_vertices(*g)) + , best_mate_vector(num_vertices(*g)) , mate(mate_vector.begin(), vm) , best_mate(best_mate_vector.begin(), vm) { vertex_iterator_t vi, vi_end; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi) best_mate[*vi] = mate[*vi] = get(arg_mate, *vi); } - template < typename PropertyMap > void find_matching(PropertyMap pm) + template void find_matching(PropertyMap pm) { edge_iterator_t ei; - boost::tie(ei, ei_end) = edges(g); + boost::tie(ei, ei_end) = edges(*g); select_edge(ei); vertex_iterator_t vi, vi_end; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi) put(pm, *vi, best_mate[*vi]); } private: - const Graph& g; + const Graph* g; VertexIndexMap vm; - std::vector< vertex_descriptor_t > mate_vector, best_mate_vector; + std::vector mate_vector, best_mate_vector; vertex_to_vertex_map_t mate, best_mate; edge_iterator_t ei_end; @@ -1265,49 +113,1379 @@ private: { if (ei == ei_end) { - if (matching_weight_sum(g, mate) - > matching_weight_sum(g, best_mate)) + if (matching_weight_sum(*g, mate) + > matching_weight_sum(*g, best_mate)) { vertex_iterator_t vi, vi_end; - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + for (boost::tie(vi, vi_end) = vertices(*g); vi != vi_end; ++vi) best_mate[*vi] = mate[*vi]; } return; } vertex_descriptor_t v, w; - v = source(*ei, g); - w = target(*ei, g); + v = source(*ei, *g); + w = target(*ei, *g); select_edge(++ei); - if (mate[v] == graph_traits< Graph >::null_vertex() - && mate[w] == graph_traits< Graph >::null_vertex()) + if (mate[v] == graph_traits::null_vertex() + && mate[w] == graph_traits::null_vertex()) { mate[v] = w; mate[w] = v; select_edge(ei); - mate[v] = mate[w] = graph_traits< Graph >::null_vertex(); + mate[v] = mate[w] = graph_traits::null_vertex(); } } }; -template < typename Graph, typename MateMap, typename VertexIndexMap > +template void brute_force_maximum_weighted_matching( const Graph& g, MateMap mate, VertexIndexMap vm) { - empty_matching< Graph, MateMap >::find_matching(g, mate); - brute_force_matching< Graph, MateMap, VertexIndexMap > brute_force_matcher( + empty_matching::find_matching(g, mate); + brute_force_matching brute_force_matcher( g, mate, vm); brute_force_matcher.find_matching(mate); } -template < typename Graph, typename MateMap > +template inline void brute_force_maximum_weighted_matching(const Graph& g, MateMap mate) { brute_force_maximum_weighted_matching(g, mate, get(vertex_index, g)); } +namespace graph +{ +namespace detail +{ + +/** Check that vertex indices are unique and in range [0, V). */ +template +void check_vertex_index_range(const Graph& g, VertexIndexMap vm) +{ + using index_t = typename property_traits::value_type; + using unsigned_index_t = typename std::make_unsigned::type; + auto nv = num_vertices(g); + std::vector got_vertex(nv); + for (const auto& x : make_iterator_range(vertices(g))) + { + index_t i = get(vm, x); + if ((i < 0) || (static_cast(i) >= nv)) + throw bad_graph("Invalid vertex index."); + if (got_vertex[i]) + throw bad_graph("Duplicate vertex index."); + got_vertex[i] = true; + } } -#endif +/** Check that edge weights are valid. */ +template +void check_maximum_weighted_matching_edge_weights( + const Graph& g, EdgeWeightMap edge_weights) +{ + for (const auto& e : make_iterator_range(edges(g))) + { + auto w = get(edge_weights, e); + auto max_weight = (std::numeric_limits::max)() / 4; + if (!(w <= max_weight)) // inverted logic to catch NaN + throw bad_graph("Edge weight exceeds maximum supported value."); + } +} + +/** Implementation of the matching algorithm. */ +template +struct maximum_weighted_matching_context +{ + using vertex_t = typename graph_traits::vertex_descriptor; + using edge_t = typename graph_traits::edge_descriptor; + using vertices_size_t = typename graph_traits::vertices_size_type; + using edges_size_t = typename graph_traits::edges_size_type; + using weight_t = typename property_traits::value_type; + + /** Ordered pair of vertices. */ + using vertex_pair_t = std::pair; + + /** + * List of edges forming an alternating path or alternating cycle. + * + * The path is defined over top-level blossoms; it skips parts of the path + * that are internal to blossoms. Vertex pairs are oriented to match the + * direction of the path. + */ + using alternating_path_t = std::deque; + + /** Top-level blossoms may be labeled "S" or "T" or unlabeled. */ + enum blossom_label_t { LABEL_NONE = 0, LABEL_S = 1, LABEL_T = 2 }; + + struct nontrivial_blossom_t; // forward declaration + + /** + * A blossom is either a single vertex, or an odd-length alternating + * cycle over sub-blossoms. + */ + struct blossom_t + { + /** Parent of this blossom, or "nullptr" for top-level blossom. */ + nontrivial_blossom_t* parent; + + /** + * Base vertex of this blossom. + * + * This is the unique vertex inside the blossom which is not matched + * to another vertex in the same blossom. + */ + vertex_t base_vertex; + + /** Label S or T or NONE, only valid for top-level blossoms. */ + blossom_label_t label; + + /** True if this is an instance of nontrivial_blossom. */ + bool is_nontrivial_blossom; + + /** Edge that attaches this blossom to the alternating tree. */ + vertex_pair_t tree_edge; + + /** Base vertex of the blossom at the root of the alternating tree. */ + vertex_t tree_root; + + /** Least-slack edge to a different S-blossom. */ + optional best_edge; + + /** Initialize a blossom instance. */ + blossom_t(vertex_t base_vertex = graph_traits::null_vertex(), + bool is_nontrivial_blossom = false) + : parent(nullptr) + , base_vertex(base_vertex) + , label(LABEL_NONE) + , is_nontrivial_blossom(is_nontrivial_blossom) + { } + + /** + * Cast this blossom_t instance to nontrivial_blossom_t if possible, + * otherwise return "nullptr". + */ + nontrivial_blossom_t* nontrivial() + { + // This is much faster than dynamic_cast. + return (is_nontrivial_blossom ? + static_cast(this) : nullptr); + } + + const nontrivial_blossom_t* nontrivial() const + { + return (is_nontrivial_blossom ? + static_cast(this) : nullptr); + } + }; + + /** + * A non-trivial blossom is an odd-length alternating cycle over + * 3 or more sub-blossoms. + */ + struct nontrivial_blossom_t : public blossom_t + { + struct sub_blossom_t { + /** Pointer to sub-blossom. */ + blossom_t* blossom; + + /** Vertex pair (x, y) where "x" is a vertex in "blossom" and + "y" is a vertex in the next sub-blossom. */ + vertex_pair_t edge; + }; + + /** List of sub-blossoms, ordered along the alternating cycle. */ + std::list subblossoms; + + /** Dual LPP variable for this blossom. */ + weight_t dual_var; + + /** Least-slack edges to other S-blossoms. */ + std::list best_edge_set; + + /** Initialize a non-trivial blossom. */ + nontrivial_blossom_t( + const std::vector& blossoms, + const std::deque& edges) + : blossom_t(blossoms.front()->base_vertex, true) + , dual_var(0) + { + BOOST_ASSERT(blossoms.size() == edges.size()); + BOOST_ASSERT(blossoms.size() % 2 == 1); + BOOST_ASSERT(blossoms.size() >= 3); + + auto blossom_it = blossoms.begin(); + auto blossom_end = blossoms.end(); + auto edge_it = edges.begin(); + while (blossom_it != blossom_end) { + subblossoms.push_back({*blossom_it, *edge_it}); + ++blossom_it; + ++edge_it; + } + } + + /** Find the position of the specified subblossom. */ + std::pair::iterator> + find_subblossom(blossom_t* child) + { + vertices_size_t pos = 0; + auto it = subblossoms.begin(); + while (it->blossom != child) { + ++it; + ++pos; + BOOST_ASSERT(it != subblossoms.end()); + } + return std::make_pair(pos, it); + } + }; + + /** Specification of a delta step. */ + struct delta_step_t + { + /** Type of delta step: 1, 2, 3 or 4. */ + int kind; + + /** Delta value. */ + weight_t value; + + /** Edge on which the minimum delta occurs (for delta type 2 or 3). */ + edge_t edge; + + /** Blossom on which the minimum delta occurs (for delta type 4). */ + nontrivial_blossom_t* blossom; + }; + + /** Similar to vector_property_map, but uses a fixed-size vector. */ + template + struct vertex_map + { + using key_type = typename property_traits::key_type; + std::vector vec; + VertexIndexMap vm; + + vertex_map(vertices_size_t arg_size, VertexIndexMap arg_vm) + : vec(arg_size) + , vm(arg_vm) + { } + + const T& operator[](const key_type& v) const + { + return vec[get(vm, v)]; + } + + T& operator[](const key_type& v) + { + return vec[get(vm, v)]; + } + }; + + /** Keep track of the least-slack edge out of a bunch of edges. */ + struct least_slack_edge_t + { + optional edge; + weight_t slack; + + least_slack_edge_t() : slack(0) {} + + void update(const edge_t& e, weight_t s) + { + if ((!edge.has_value()) || (s < slack)) + { + edge = e; + slack = s; + } + } + }; + + /** Scale integer edge weights to enable integer-only calculations. */ + static constexpr weight_t weight_factor = + std::numeric_limits::is_integer ? 2 : 1; + + /** Input graph. */ + const Graph* g; + VertexIndexMap vm; + EdgeWeightMap edge_weights; + + /** Current mate of each vertex. */ + vertex_map vertex_mate; + + /** + * For each vertex, the trivial blossom that contains it. + * + * Pointers to blossoms must remain valid for the life time of + * this data structure, therefore the underlying vector must + * have a fixed size. + */ + vertex_map trivial_blossom; + + /** + * List of non-trivial blossoms. + * + * This must be a linked list to ensure that elements can be added + * and removed without invalidating pointers to other elements. + */ + std::list nontrivial_blossom; + + /** For each vertex, the unique top-level blossom that contains it. */ + vertex_map vertex_top_blossom; + + /** For each vertex, a variable in the dual LPP. */ + vertex_map vertex_dual; + + /** For T-vertex or unlabeled vertex, least-slack edge to any S-vertex. */ + vertex_map> vertex_best_edge; + + /** Queue of S-vertices to be scanned. */ + std::deque scan_queue; + + /** Initialize the matching algorithm. */ + explicit maximum_weighted_matching_context( + const Graph& arg_g, VertexIndexMap arg_vm, EdgeWeightMap weights) + : g(&arg_g) + , vm(arg_vm) + , edge_weights(weights) + , vertex_mate(num_vertices(*g), arg_vm) + , trivial_blossom(num_vertices(*g), arg_vm) + , vertex_top_blossom(num_vertices(*g), arg_vm) + , vertex_dual(num_vertices(*g), arg_vm) + , vertex_best_edge(num_vertices(*g), arg_vm) + { + // Vertex duals are initialized to half the maximum edge weight. + weight_t max_weight = 0; + for (const edge_t& e : make_iterator_range(edges(*g))) + max_weight = (std::max)(max_weight, get(weights, e)); + weight_t init_vertex_dual = max_weight * (weight_factor / 2); + + for (const vertex_t& x : make_iterator_range(vertices(*g))) + { + vertex_mate[x] = null_vertex(); + trivial_blossom[x].base_vertex = x; + vertex_top_blossom[x] = &trivial_blossom[x]; + vertex_dual[x] = init_vertex_dual; + } + } + + /** Return the null vertex. */ + static vertex_t null_vertex() + { + return graph_traits::null_vertex(); + } + + /** Call a function for every vertex inside the specified blossom. */ + template + static void for_vertices_in_blossom(const blossom_t& blossom, Func func) + { + auto ntb = blossom.nontrivial(); + if (ntb) { + // Visit all vertices in the non-trivial blossom. + // Use an explicit stack to avoid deep call chains. + std::vector stack; + stack.push_back(ntb); + while (!stack.empty()) { + auto cur = stack.back(); + stack.pop_back(); + for (const auto& sub : cur->subblossoms) { + ntb = sub.blossom->nontrivial(); + if (ntb) { + stack.push_back(ntb); + } else { + func(sub.blossom->base_vertex); + } + } + } + } else { + // A trivial blossom contains just one vertex. + func(blossom.base_vertex); + } + } + + /** Mark blossom as the top-level blossom of its vertices. */ + void update_top_level_blossom(blossom_t& blossom) + { + BOOST_ASSERT(!blossom.parent); + for_vertices_in_blossom(blossom, + [this,&blossom](vertex_t x) + { + vertex_top_blossom[x] = &blossom; + }); + } + + /** + * Calculate edge slack. + * The two vertices must be in different top-level blossoms. + */ + weight_t edge_slack(const edge_t& e) const + { + vertex_t x = source(e, *g); + vertex_t y = target(e, *g); + weight_t w = get(edge_weights, e); + BOOST_ASSERT(vertex_top_blossom[x] != vertex_top_blossom[y]); + return vertex_dual[x] + vertex_dual[y] - weight_factor * w; + } + + /** Clear least-slack edge tracking. */ + void clear_best_edge() + { + for (vertex_t x : make_iterator_range(vertices(*g))) + { + vertex_best_edge[x].reset(); + trivial_blossom[x].best_edge.reset(); + } + + for (nontrivial_blossom_t& b : nontrivial_blossom) + { + b.best_edge.reset(); + b.best_edge_set.clear(); + } + } + + /** Add edge from unlabeled verter or T-vertex "y" to an S-vertex. */ + void add_delta2_edge(vertex_t y, const edge_t& e, weight_t slack) + { + auto& best_edge = vertex_best_edge[y]; + if ((!best_edge.has_value()) || slack < edge_slack(*best_edge)) + best_edge = e; + } + + /** Return least-slack edge between any unlabeled vertex and S-vertex. */ + least_slack_edge_t get_least_slack_delta2_edge() + { + least_slack_edge_t best_edge; + for (vertex_t x : make_iterator_range(vertices(*g))) + { + if (vertex_top_blossom[x]->label == LABEL_NONE) + { + const auto& vertex_edge = vertex_best_edge[x]; + if (vertex_edge.has_value()) + best_edge.update(*vertex_edge, edge_slack(*vertex_edge)); + } + } + return best_edge; + } + + /** Add edge between different S-blossoms. */ + void add_delta3_edge(blossom_t& b, const edge_t& e, weight_t slack) + { + auto& best_edge = b.best_edge; + if ((!best_edge.has_value()) || slack < edge_slack(*best_edge)) + best_edge = e; + + auto ntb = b.nontrivial(); + if (ntb) + ntb->best_edge_set.push_back(e); + } + + /** Update least-slack edge tracking after merging blossoms. */ + void merge_delta3_blossoms(nontrivial_blossom_t& blossom) + { + // Build a temporary array holding the least-slack edges to + // other S-blossoms. The array is indexed by base vertex. + std::vector tmp_best_edge(num_vertices(*g)); + + // Collect edges from sub-blossoms that were S-blossoms. + for (auto& sub : blossom.subblossoms) + { + blossom_t* b = sub.blossom; + if (b->label == LABEL_S) + { + b->best_edge.reset(); + auto ntb = b->nontrivial(); + if (ntb) + { + // Use least-slack edges from subblossom. + for (const edge_t& e : ntb->best_edge_set) + { + blossom_t* bx = vertex_top_blossom[source(e, *g)]; + blossom_t* by = vertex_top_blossom[target(e, *g)]; + BOOST_ASSERT(bx == &blossom); + // Only use edges between top-level blossoms. + if (bx != by) + { + BOOST_ASSERT(by->label == LABEL_S); + tmp_best_edge[get(vm, by->base_vertex)].update( + e, edge_slack(e)); + } + } + ntb->best_edge_set.clear(); + } + else + { + // Trivial blossoms don't maintain a least-slack edge set. + // Consider all incident edges. + for (const edge_t& e : + make_iterator_range(out_edges(b->base_vertex, *g))) + { + BOOST_ASSERT(source(e, *g) == b->base_vertex); + vertex_t y = target(e, *g); + blossom_t* by = vertex_top_blossom[y]; + // Only use edges to different S-blossoms. + // Ignore edges with negative weight. + if ((by != &blossom) + && (by->label == LABEL_S) + && (get(edge_weights, e) >= 0)) + { + tmp_best_edge[get(vm, by->base_vertex)].update( + e, edge_slack(e)); + } + } + } + } + } + + // Build a compact list of edges from the temporary array. + // Also find the overall least-slack edge to any other S-blossom. + BOOST_ASSERT(blossom.best_edge_set.empty()); + BOOST_ASSERT(!blossom.best_edge.has_value()); + least_slack_edge_t best_edge; + for (const least_slack_edge_t& item : tmp_best_edge) + { + if (item.edge.has_value()) + { + blossom.best_edge_set.push_back(*item.edge); + best_edge.update(*item.edge, item.slack); + } + } + blossom.best_edge = best_edge.edge; + } + + /** Return least-slack edge between any pair of S-blossoms. */ + least_slack_edge_t get_least_slack_delta3_edge() + { + least_slack_edge_t best_edge; + + for (vertex_t x : make_iterator_range(vertices(*g))) + { + blossom_t* b = vertex_top_blossom[x]; + BOOST_ASSERT(!b->parent); + if ((b->label == LABEL_S) && (b->best_edge.has_value())) + best_edge.update(*b->best_edge, edge_slack(*b->best_edge)); + } + + return best_edge; + } + + /** Add the vertices in a blossom to the scan queue. */ + void add_vertices_to_scan_queue(blossom_t& blossom) + { + for_vertices_in_blossom(blossom, + [this](vertex_t x) + { + scan_queue.push_back(x); + }); + } + + /** Trace back through the alternating trees from vertices "x" and "y". */ + alternating_path_t trace_alternating_paths(vertex_t x, vertex_t y) + { + // Initialize a path containing only the edge (x, y). + alternating_path_t path; + path.emplace_back(x, y); + + // Trace alternating path from "x" to the root of the tree. + while (x != null_vertex()) + { + blossom_t* bx = vertex_top_blossom[x]; + x = bx->tree_edge.first; + if (x != null_vertex()) + path.push_front(bx->tree_edge); + } + + // Trace alternating path from "y" to the root of the tree. + while (y != null_vertex()) + { + blossom_t* by = vertex_top_blossom[y]; + y = by->tree_edge.first; + if (y != null_vertex()) + path.emplace_back(by->tree_edge.second, y); + } + + // If we found a common ancestor, trim the paths so they end there. + while (path.front().second == path.back().first) + { + BOOST_ASSERT(path.size() > 2); + path.pop_front(); + path.pop_back(); + } + + // Any alternating path between S-blossoms must have odd length. + BOOST_ASSERT(path.size() % 2 == 1); + + return path; + } + + /** Create a new S-blossom from an alternating cycle. */ + void make_blossom(const alternating_path_t& path) + { + BOOST_ASSERT(path.size() % 2 == 1); + BOOST_ASSERT(path.size() >= 3); + + // Collect pointers to sub-blossoms. + std::vector subblossoms; + subblossoms.reserve(path.size()); + for (const vertex_pair_t& edge : path) + subblossoms.push_back(vertex_top_blossom[edge.first]); + + // Check that the path is cyclic. + vertices_size_t pos = 0; + for (const vertex_pair_t& edge : path) + { + pos = (pos + 1) % subblossoms.size(); + BOOST_ASSERT(vertex_top_blossom[edge.second] == subblossoms[pos]); + } + + // Create the new blossom. + nontrivial_blossom.emplace_back(subblossoms, path); + nontrivial_blossom_t& blossom = nontrivial_blossom.back(); + + // Link the subblossoms to the new parent. + // Insert former T-vertices into the scan queue. + for (blossom_t* b : subblossoms) + { + BOOST_ASSERT(!b->parent); + b->parent = &blossom; + if (b->label == LABEL_T) + add_vertices_to_scan_queue(*b); + } + + // Mark vertices as belonging to the new blossom. + update_top_level_blossom(blossom); + + // Assign label S to the new blossom and link to the alternating tree. + BOOST_ASSERT(subblossoms.front()->label == LABEL_S); + blossom.label = LABEL_S; + blossom.tree_edge = subblossoms.front()->tree_edge; + blossom.tree_root = subblossoms.front()->tree_root; + + // Merge least-slack edges for the S-sub-blossoms. + merge_delta3_blossoms(blossom); + } + + /** Expand and delete a T-blossom. */ + void expand_t_blossom(nontrivial_blossom_t* blossom) + { + BOOST_ASSERT(!blossom->parent); + BOOST_ASSERT(blossom->label == LABEL_T); + + // Convert sub-blossoms into top-level blossoms. + for (const auto& sub : blossom->subblossoms) + { + blossom_t* b = sub.blossom; + BOOST_ASSERT(b->parent == blossom); + b->parent = nullptr; + b->label = LABEL_NONE; + update_top_level_blossom(*b); + } + + // Reconstruct the alternating tree via the sub-blossoms. + // Find the sub-blossom that attaches the expanding blossom to + // the alternating tree. + blossom_t* entry = vertex_top_blossom[blossom->tree_edge.second]; + + auto subblossom_loc = blossom->find_subblossom(entry); + auto entry_pos = subblossom_loc.first; + auto entry_it = subblossom_loc.second; + + // Get the edge that attached this blossom to the alternating tree. + vertex_t x, y; + std::tie(x, y) = blossom->tree_edge; + + // Reconstruct the tree in an even number of steps from entry to base. + auto sub_it = entry_it; + if (entry_pos % 2 == 0) + { + // Walk backward around the blossom. + auto sub_begin = blossom->subblossoms.begin(); + while (sub_it != sub_begin) + { + extend_tree_s_to_t(x, y); + --sub_it; + BOOST_ASSERT(sub_it != sub_begin); + --sub_it; + std::tie(y, x) = sub_it->edge; + } + } + else + { + // Walk forward around the blossom. + auto sub_end = blossom->subblossoms.end(); + while (sub_it != sub_end) { + extend_tree_s_to_t(x, y); + ++sub_it; + BOOST_ASSERT(sub_it != sub_end); + std::tie(x, y) = sub_it->edge; + ++sub_it; + } + } + + // Assign label T to the base sub-blossom. + blossom_t* base = blossom->subblossoms.front().blossom; + base->label = LABEL_T; + base->tree_edge = std::make_pair(x, y); + base->tree_root = blossom->tree_root; + + // Delete the expanded blossom. + auto blossom_it = std::find_if( + nontrivial_blossom.begin(), + nontrivial_blossom.end(), + [blossom](const nontrivial_blossom_t& b) + { + return (&b == blossom); + }); + BOOST_ASSERT(blossom_it != nontrivial_blossom.end()); + nontrivial_blossom.erase(blossom_it); + } + + void augment_blossom_rec( + nontrivial_blossom_t& blossom, blossom_t& entry, + std::stack>& stack) + { + auto subblossom_loc = blossom.find_subblossom(&entry); + auto entry_pos = subblossom_loc.first; + auto entry_it = subblossom_loc.second; + + // Walk from entry to the base in an even number of steps. + auto sub_begin = blossom.subblossoms.begin(); + auto sub_end = blossom.subblossoms.end(); + auto sub_it = entry_it; + while ((sub_it != sub_begin) && (sub_it != sub_end)) { + vertex_t x, y; + blossom_t* bx; + blossom_t* by; + + if (entry_pos % 2 == 0) + { + // Walk backward around the blossom. + --sub_it; + by = sub_it->blossom; + BOOST_ASSERT(sub_it != sub_begin); + --sub_it; + bx = sub_it->blossom; + std::tie(x, y) = sub_it->edge; + } + else + { + // Walk forward around the blossom. + ++sub_it; + BOOST_ASSERT(sub_it != sub_end); + std::tie(x, y) = sub_it->edge; + bx = sub_it->blossom; + ++sub_it; + by = (sub_it == sub_end) ? + blossom.subblossoms.front().blossom : + sub_it->blossom; + } + + // Pull this edge into the matching. + vertex_mate[x] = y; + vertex_mate[y] = x; + + // Augment through any non-trivial subblossoms touching this edge. + auto bx_ntb = bx->nontrivial(); + if (bx_ntb) + stack.emplace(bx_ntb, &trivial_blossom[x]); + + auto by_ntb = by->nontrivial(); + if (by_ntb) + stack.emplace(by_ntb, &trivial_blossom[y]); + } + + // Re-orient the blossom. + if (entry_it != sub_begin) + blossom.subblossoms.splice( + sub_begin, blossom.subblossoms, entry_it, sub_end); + blossom.base_vertex = entry.base_vertex; + } + + void augment_blossom(nontrivial_blossom_t& blossom, blossom_t& entry) + { + // Use an explicit stack to avoid deep call chains. + std::stack> stack; + stack.emplace(&blossom, &entry); + + while (!stack.empty()) { + nontrivial_blossom_t* outer_blossom; + blossom_t* inner_entry; + std::tie(outer_blossom, inner_entry) = stack.top(); + + nontrivial_blossom_t* inner_blossom = inner_entry->parent; + BOOST_ASSERT(inner_blossom); + + if (inner_blossom != outer_blossom) + stack.top() = std::make_pair(outer_blossom, inner_blossom); + else + stack.pop(); + + augment_blossom_rec(*inner_blossom, *inner_entry, stack); + } + } + + /** Augment the matching through the specified augmenting path. */ + void augment_matching(const alternating_path_t& path) + { + BOOST_ASSERT(path.size() % 2 == 1); + + // Process the unmatched edges on the augmenting path. + auto path_it = path.begin(); + auto path_end = path.end(); + while (path_it != path_end) + { + vertex_t x = path_it->first; + vertex_t y = path_it->second; + + // Augment any non-trivial blossoms that touch this edge. + blossom_t* bx = vertex_top_blossom[x]; + auto bx_ntb = bx->nontrivial(); + if (bx_ntb) + augment_blossom(*bx_ntb, trivial_blossom[x]); + + blossom_t* by = vertex_top_blossom[y]; + auto by_ntb = by->nontrivial(); + if (by_ntb) + augment_blossom(*by_ntb, trivial_blossom[y]); + + // Pull this edge into the matching. + vertex_mate[x] = y; + vertex_mate[y] = x; + + // Go to the next unmatched edge on the path. + ++path_it; + if (path_it == path_end) + break; + ++path_it; + } + } + + /** + * Remove non-S-vertices from the scan queue. + * This is necessary after removing labels from S-blossoms. + */ + void refresh_scan_queue() + { + std::deque new_scan_queue; + for (const vertex_t& x : scan_queue) + { + if (vertex_top_blossom[x]->label == LABEL_S) + new_scan_queue.push_back(x); + } + scan_queue = std::move(new_scan_queue); + } + + /** Remove edges to non-S-vertices from delta3 edge tracking. */ + void refresh_delta3_blossom(blossom_t& b) + { + // Do nothing if this blossom was not tracking any delta3 edge. + if (!b.best_edge.has_value()) + return; + + auto ntb = b.nontrivial(); + if (ntb) + { + // Remove bad edges from best_edge_set and refresh best_edge. + least_slack_edge_t best_edge; + auto it = ntb->best_edge_set.begin(); + auto it_end = ntb->best_edge_set.end(); + while (it != it_end) + { + vertex_t y = target(*it, *g); + blossom_t* by = vertex_top_blossom[y]; + BOOST_ASSERT(by != &b); + if (by->label == LABEL_S) + { + best_edge.update(*it, edge_slack(*it)); + ++it; + } + else + { + // Remove edge to non-S-blossom. + it = ntb->best_edge_set.erase(it); + } + } + b.best_edge = best_edge.edge; + } + else + { + // Trivial blossom does not maintain best_edge_set. + // If its current best_edge is invalid, recompute it. + vertex_t x = b.base_vertex; + vertex_t y = target(*b.best_edge, *g); + blossom_t* by = vertex_top_blossom[y]; + BOOST_ASSERT(by != &b); + if (by->label != LABEL_S) + { + // Consider all incident edges to recompute best_edge. + least_slack_edge_t best_edge; + for (const edge_t& e : make_iterator_range(out_edges(x, *g))) + { + BOOST_ASSERT(source(e, *g) == x); + y = target(e, *g); + by = vertex_top_blossom[y]; + if ((by != &b) && (by->label == LABEL_S)) + best_edge.update(e, edge_slack(e)); + } + b.best_edge = best_edge.edge; + } + } + } + + /** Recompute vertex_best_edge for the specified vertex. */ + void recompute_vertex_best_edge(vertex_t x) + { + least_slack_edge_t best_edge; + + for (const edge_t& e : make_iterator_range(out_edges(x, *g))) + { + BOOST_ASSERT(source(e, *g) == x); + vertex_t y = target(e, *g); + blossom_t* by = vertex_top_blossom[y]; + if (by->label == LABEL_S) + best_edge.update(e, edge_slack(e)); + } + vertex_best_edge[x] = best_edge.edge; + } + + /** Remove the alternating trees with specified root vertices. */ + void remove_alternating_tree(vertex_t r1, vertex_t r2) + { + // Find blossoms that are part of the specified alternating trees. + std::vector former_s_blossoms; + for (vertex_t x : make_iterator_range(vertices(*g))) + { + blossom_t* b = vertex_top_blossom[x]; + if ((!b->parent) + && (b->label != LABEL_NONE) + && (b->tree_root == r1 || b->tree_root == r2)) + { + // Build list of former S-blossoms. + if (b->label == LABEL_S) + former_s_blossoms.push_back(b); + + // Remove label. + b->label = LABEL_NONE; + } + } + + vertex_map blossom_recompute_best_edge(num_vertices(*g), vm); + vertex_map vertex_recompute_best_edge(num_vertices(*g), vm); + + // Visit former S-blossoms. + for (blossom_t* b : former_s_blossoms) + { + // Clear best_edge tracking. + b->best_edge.reset(); + auto ntb = b->nontrivial(); + if (ntb) + ntb->best_edge_set.clear(); + + // Visit edges that touch this blossom. + for_vertices_in_blossom(*b, + [&](vertex_t x) + { + // Mark former S-vertices. + vertex_recompute_best_edge[x] = 1; + + for (const edge_t& e : + make_iterator_range(out_edges(x, *g))) + { + // Mark S-blossoms adjacent to the former S-blossom. + BOOST_ASSERT(source(e, *g) == x); + vertex_t y = target(e, *g); + blossom_t* by = vertex_top_blossom[y]; + if (by->label == LABEL_S) + blossom_recompute_best_edge[by->base_vertex] = 1; + + // Mark non-S-vertices with least-slack edge to + // former S-blossom. + if (by->label != LABEL_S) + { + const auto& best_edge = vertex_best_edge[y]; + if (best_edge.has_value() && (*best_edge == e)) + vertex_recompute_best_edge[y] = 1; + } + } + }); + } + + // Recompute delta3 tracking of affected S-blossoms. + for (vertex_t x : make_iterator_range(vertices(*g))) + { + if (blossom_recompute_best_edge[x]) + refresh_delta3_blossom(*vertex_top_blossom[x]); + } + + // Recompute vertex_best_edge of affected non-S-vertices. + for (vertex_t x : make_iterator_range(vertices(*g))) + { + if (vertex_recompute_best_edge[x]) + recompute_vertex_best_edge(x); + } + + refresh_scan_queue(); + } + + /** + * Extend the alternating tree via the edge from S-vertex "x" + * to unlabeled vertex "y". + * + * Assign label T to the blossom that contains "y", then assign + * label S to the blossom matched to the newly labeled T-blossom. + */ + void extend_tree_s_to_t(vertex_t x, vertex_t y) + { + blossom_t* bx = vertex_top_blossom[x]; + blossom_t* by = vertex_top_blossom[y]; + + BOOST_ASSERT(bx->label == LABEL_S); + BOOST_ASSERT(by->label == LABEL_NONE); + by->label = LABEL_T; + by->tree_edge = std::make_pair(x, y); + by->tree_root = bx->tree_root; + + vertex_t y2 = by->base_vertex; + vertex_t z = vertex_mate[y2]; + BOOST_ASSERT(z != null_vertex()); + + blossom_t* bz = vertex_top_blossom[z]; + BOOST_ASSERT(bz->label == LABEL_NONE); + BOOST_ASSERT(!bz->best_edge.has_value()); + bz->label = LABEL_S; + bz->tree_edge = std::make_pair(y2, z); + bz->tree_root = by->tree_root; + add_vertices_to_scan_queue(*bz); + } + + /** + * Add the edge between S-vertices "x" and "y". + * + * If the edge connects blossoms that are part of the same alternating + * tree, a new S-blossom is created. + * + * If the edge connects two different alternating trees, an augmenting + * path has been discovered. In this case the matching is augmented. + * + * @return true if the matching was augmented; otherwise false. + */ + bool add_s_to_s_edge(vertex_t x, vertex_t y) + { + blossom_t* bx = vertex_top_blossom[x]; + blossom_t* by = vertex_top_blossom[y]; + BOOST_ASSERT(bx->label == LABEL_S); + BOOST_ASSERT(by->label == LABEL_S); + BOOST_ASSERT(bx != by); + + alternating_path_t path = trace_alternating_paths(x, y); + + // Check whether the path starts and ends in the same blossom. + blossom_t* b1 = vertex_top_blossom[path.front().first]; + blossom_t* b2 = vertex_top_blossom[path.back().second]; + if (b1 == b2) + { + make_blossom(path); + return false; + } + else + { + // Remove labels the two alternating trees on the augmenting path. + remove_alternating_tree(bx->tree_root, by->tree_root); + + // Augment matching. + augment_matching(path); + return true; + } + } + + /** + * Scan incident edges of newly labeled S-vertices. + * + * @return true if the matching was augmented; otherwise false. + */ + bool scan_new_s_vertices() + { + while (!scan_queue.empty()) + { + vertex_t x = scan_queue.front(); + scan_queue.pop_front(); + + BOOST_ASSERT(vertex_top_blossom[x]->label == LABEL_S); + + for (const edge_t& e : make_iterator_range(out_edges(x, *g))) + { + BOOST_ASSERT(source(e, *g) == x); + vertex_t y = target(e, *g); + + // Note: top level blossom of x may change during this loop, + // so we must look it up on each pass. + blossom_t* bx = vertex_top_blossom[x]; + blossom_t* by = vertex_top_blossom[y]; + + // Ignore internal edges in blossom. + if (bx == by) + continue; + + // Ignore edges with negative slack to prevent numeric overflow. + if (get(edge_weights, e) < 0) + continue; + + weight_t slack = edge_slack(e); + if (slack <= 0) + { + // Tight edge. + if (by->label == LABEL_NONE) + extend_tree_s_to_t(x, y); + else if (by->label == LABEL_S) + { + bool augmented = add_s_to_s_edge(x, y); + if (augmented) + return true; + } + } + else + { + // Track non-tight edges between S-blossoms. + if (by->label == LABEL_S) + add_delta3_edge(*bx, e, slack); + } + + // Track all edges between S-blossom and non-S-blossom. + if (by->label != LABEL_S) + add_delta2_edge(y, e, slack); + } + } + + return false; + } + + /** Calculate a delta step in the dual LPP problem. */ + delta_step_t calc_dual_delta_step() + { + delta_step_t delta{}; + + // Compute delta1: minimum dual variable of any S-vertex. + delta.kind = 1; + delta.value = (std::numeric_limits::max)(); + for (vertex_t x : make_iterator_range(vertices(*g))) + { + if (vertex_top_blossom[x]->label == LABEL_S) + delta.value = (std::min)(delta.value, vertex_dual[x]); + } + + // Compute delta2: minimum slack of edge from S-vertex to unlabeled. + least_slack_edge_t best_edge = get_least_slack_delta2_edge(); + if (best_edge.edge.has_value() && (best_edge.slack <= delta.value)) + { + delta.kind = 2; + delta.edge = *best_edge.edge; + delta.value = best_edge.slack; + } + + // Compute delta3: half minimum slack of edge between S-blossoms. + best_edge = get_least_slack_delta3_edge(); + if (best_edge.edge.has_value() && (best_edge.slack / 2 <= delta.value)) + { + delta.kind = 3; + delta.edge = *best_edge.edge; + delta.value = best_edge.slack / 2; + } + + // Compute delta4: half minimum dual of a top-level T-blossom. + for (nontrivial_blossom_t& blossom : nontrivial_blossom) + { + if ((!blossom.parent) + && (blossom.label == LABEL_T) + && (blossom.dual_var / 2 <= delta.value)) + { + delta.kind = 4; + delta.blossom = &blossom; + delta.value = blossom.dual_var / 2; + } + } + + return delta; + } + + /** Apply a delta step to the dual LPP variables. */ + void apply_delta_step(weight_t delta) + { + for (vertex_t x : make_iterator_range(vertices(*g))) + { + blossom_t* b = vertex_top_blossom[x]; + if (b->label == LABEL_S) + vertex_dual[x] -= delta; + else if (b->label == LABEL_T) + vertex_dual[x] += delta; + } + + for (nontrivial_blossom_t& blossom : nontrivial_blossom) + { + if (!blossom.parent) + { + if (blossom.label == LABEL_S) + blossom.dual_var += 2 * delta; + else if (blossom.label == LABEL_T) + blossom.dual_var -= 2 * delta; + } + } + } + + /** + * Run one stage of the matching algorithm. + * + * @return True if the matching was successfully augmented; + * false if no further improvement is possible. + */ + bool run_stage() + { + // Run substages. + while (true) { + bool augmented = scan_new_s_vertices(); + if (augmented) + return true; + + delta_step_t delta = calc_dual_delta_step(); + apply_delta_step(delta.value); + + if (delta.kind == 2) + { + vertex_t x = source(delta.edge, *g); + vertex_t y = target(delta.edge, *g); + if (vertex_top_blossom[x]->label != LABEL_S) + std::swap(x, y); + extend_tree_s_to_t(x, y); + } + else if (delta.kind == 3) + { + vertex_t x = source(delta.edge, *g); + vertex_t y = target(delta.edge, *g); + augmented = add_s_to_s_edge(x, y); + if (augmented) + return true; + } + else if (delta.kind == 4) + { + BOOST_ASSERT(delta.blossom); + expand_t_blossom(delta.blossom); + } + else + { + // No further improvement possible. + BOOST_ASSERT(delta.kind == 1); + return false; + } + } + } + + /** Run the matching algorithm. */ + void run() + { + // Assign label S to all vertices and put them in the queue. + for (vertex_t x : make_iterator_range(vertices(*g))) + { + BOOST_ASSERT(vertex_mate[x] == null_vertex()); + blossom_t* bx = vertex_top_blossom[x]; + BOOST_ASSERT(bx->label == LABEL_NONE); + BOOST_ASSERT(bx->base_vertex == x); + bx->label = LABEL_S; + bx->tree_edge = std::make_pair(null_vertex(), x); + bx->tree_root = x; + scan_queue.push_back(x); + } + + // Improve the solution until no further improvement is possible. + while (run_stage()) ; + + // Clear temporary data structures. + scan_queue.clear(); + clear_best_edge(); + } + + /** Copy matching to specified map. */ + template + void extract_matching(MateMap mate) + { + for (vertex_t x : make_iterator_range(vertices(*g))) + put(mate, x, vertex_mate[x]); + } +}; + +} // namespace detail +} // namespace graph + +/** + * Compute a maximum-weighted matching in an undirected weighted graph. + * + * This function takes time O(V^3). + * This function uses memory size O(V + E). + * + * @param g Input graph. + * The graph type must be a model of VertexListGraph + * and EdgeListGraph and IncidenceGraph. + * The graph must be undirected. + * The graph may not contain parallel edges. + * + * @param mate ReadWritePropertyMap, mapping vertices to vertices. + * This map returns the result of the computation. + * For each vertex "v", "mate[v]" is the vertex that is + * matched to "v", or "null_vertex()" if "v" is not matched. + * + * @param vm ReadablePropertyMap, mapping vertices to indexes + * in range [0, num_vertices(g)). + * + * @param weights ReadablePropertyMap, mapping edges to edge weights. + * Edge weights must be integers or floating point values. + * + * @throw boost::bad_graph If the input graph is not valid. + */ +template +void maximum_weighted_matching( + const Graph& g, MateMap mate, VertexIndexMap vm, EdgeWeightMap weights) +{ + BOOST_CONCEPT_ASSERT((VertexListGraphConcept)); + BOOST_CONCEPT_ASSERT((EdgeListGraphConcept)); + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept)); + BOOST_STATIC_ASSERT(is_undirected_graph::value); + + using vertex_t = typename graph_traits::vertex_descriptor; + using edge_t = typename graph_traits::edge_descriptor; + BOOST_CONCEPT_ASSERT((ReadWritePropertyMapConcept)); + BOOST_CONCEPT_ASSERT( + (ReadablePropertyMapConcept)); + BOOST_CONCEPT_ASSERT((ReadablePropertyMapConcept)); + + graph::detail::check_vertex_index_range(g, vm); + graph::detail::check_maximum_weighted_matching_edge_weights(g, weights); + + graph::detail::maximum_weighted_matching_context< + Graph, VertexIndexMap, EdgeWeightMap> + matching(g, vm, weights); + matching.run(); + matching.extract_matching(mate); +} + +/** + * Compute a maximum-weighted matching in an undirected weighted graph. + * + * This overloaded function obtains edge weights from "get(edge_weight, g)". + */ +template +inline void maximum_weighted_matching( + const Graph& g, MateMap mate, VertexIndexMap vm) +{ + maximum_weighted_matching(g, mate, vm, get(edge_weight, g)); +} + +/** + * Compute a maximum-weighted matching in an undirected weighted graph. + * + * This overloaded function obtains vertex indices from "get(vertex_index, g)" + * and edge weights from "get(edge_weight, g)". + */ +template +inline void maximum_weighted_matching(const Graph& g, MateMap mate) +{ + maximum_weighted_matching(g, mate, get(vertex_index, g)); +} + +} // namespace boost + +#endif // BOOST_GRAPH_MAXIMUM_WEIGHTED_MATCHING_HPP diff --git a/include/boost/graph/named_graph.hpp b/include/boost/graph/named_graph.hpp index 991458be..85dce275 100644 --- a/include/boost/graph/named_graph.hpp +++ b/include/boost/graph/named_graph.hpp @@ -275,12 +275,12 @@ namespace graph /// Extract the name from a vertex property instance typename extract_name_type::result_type extract_name( - const bundled_vertex_property_type& property); + const bundled_vertex_property_type& property) const; /// Search for a vertex that has the given property (based on its /// name) optional< vertex_descriptor > vertex_by_property( - const bundled_vertex_property_type& property); + const bundled_vertex_property_type& property) const; /// Mapping from names to vertices named_vertices_type named_vertices; @@ -340,7 +340,7 @@ namespace graph template < BGL_NAMED_GRAPH_PARAMS > typename BGL_NAMED_GRAPH::extract_name_type::result_type - BGL_NAMED_GRAPH::extract_name(const bundled_vertex_property_type& property) + BGL_NAMED_GRAPH::extract_name(const bundled_vertex_property_type& property) const { return named_vertices.key_extractor().extract(property); } @@ -348,7 +348,7 @@ namespace graph template < BGL_NAMED_GRAPH_PARAMS > optional< typename BGL_NAMED_GRAPH::vertex_descriptor > BGL_NAMED_GRAPH::vertex_by_property( - const bundled_vertex_property_type& property) + const bundled_vertex_property_type& property) const { return find_vertex(extract_name(property), *this); } diff --git a/include/boost/graph/transitive_closure.hpp b/include/boost/graph/transitive_closure.hpp index fa8a7cd9..bd328efd 100644 --- a/include/boost/graph/transitive_closure.hpp +++ b/include/boost/graph/transitive_closure.hpp @@ -11,9 +11,8 @@ #include #include // for std::min and std::max -#include +#include //for std::less #include -#include #include #include #include @@ -130,16 +129,16 @@ void transitive_closure(const Graph& g, GraphTC& tc, std::vector< std::vector< cg_vertex > > CG_vec(num_vertices(CG)); for (size_type i = 0; i < num_vertices(CG); ++i) { - using namespace boost::placeholders; - typedef typename boost::graph_traits< CG_t >::adjacency_iterator cg_adj_iter; std::pair< cg_adj_iter, cg_adj_iter > pr = adjacent_vertices(i, CG); CG_vec[i].assign(pr.first, pr.second); std::sort(CG_vec[i].begin(), CG_vec[i].end(), - boost::bind(std::less< cg_vertex >(), - boost::bind(detail::subscript(topo_number), _1), - boost::bind(detail::subscript(topo_number), _2))); + [&topo_number](const auto& cg_0, const auto& cg_1) + { + return std::less< cg_vertex >()( + topo_number[cg_0], topo_number[cg_1]); + }); } std::vector< std::vector< cg_vertex > > chains; @@ -151,7 +150,7 @@ void transitive_closure(const Graph& g, GraphTC& tc, cg_vertex v = *i; if (!in_a_chain[v]) { - chains.resize(chains.size() + 1); + chains.emplace_back(); std::vector< cg_vertex >& chain = chains.back(); for (;;) { diff --git a/include/boost/graph/undirected_dfs.hpp b/include/boost/graph/undirected_dfs.hpp index 62088c87..bfb756a7 100644 --- a/include/boost/graph/undirected_dfs.hpp +++ b/include/boost/graph/undirected_dfs.hpp @@ -78,10 +78,10 @@ namespace detail if (v_color == Color::white()) { vis.tree_edge(*ei, g); - src_e = *ei; stack.push_back(std::make_pair(u, - std::make_pair(src_e, std::make_pair(++ei, ei_end)))); + std::make_pair(src_e, std::make_pair(std::next(ei), ei_end)))); u = v; + src_e = *ei; put(vertex_color, u, Color::gray()); vis.discover_vertex(u, g); boost::tie(ei, ei_end) = out_edges(u, g); diff --git a/include/boost/graph/vector_as_graph.hpp b/include/boost/graph/vector_as_graph.hpp index 444df876..1797dac4 100644 --- a/include/boost/graph/vector_as_graph.hpp +++ b/include/boost/graph/vector_as_graph.hpp @@ -283,7 +283,7 @@ void remove_edge_if(Predicate p, std::vector< EdgeList, Allocator >& g) template < class EdgeList, class Allocator > typename EdgeList::value_type add_vertex(std::vector< EdgeList, Allocator >& g) { - g.resize(g.size() + 1); + g.emplace_back(); return g.size() - 1; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9048b24f..c7a6ae82 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -120,6 +120,7 @@ alias graph_test_regular : [ run king_ordering.cpp ] [ run matching_test.cpp ] [ run weighted_matching_test.cpp ] + [ run weighted_matching_test2.cpp ] [ run max_flow_test.cpp ] [ run boykov_kolmogorov_max_flow_test.cpp ] [ run cycle_ratio_tests.cpp /boost/graph//boost_graph : $(CYCLE_RATIO_INPUT_FILE) ] diff --git a/test/adj_matrix_cc.cpp b/test/adj_matrix_cc.cpp index 9457a70c..68d024ca 100644 --- a/test/adj_matrix_cc.cpp +++ b/test/adj_matrix_cc.cpp @@ -21,6 +21,7 @@ int main(int, char*[]) BOOST_CONCEPT_ASSERT((EdgeListGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((MutableGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyMatrixConcept< Graph >)); } @@ -30,6 +31,7 @@ int main(int, char*[]) BOOST_CONCEPT_ASSERT((EdgeListGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((MutableGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyMatrixConcept< Graph >)); } @@ -44,6 +46,7 @@ int main(int, char*[]) BOOST_CONCEPT_ASSERT((EdgeListGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyMatrixConcept< Graph >)); BOOST_CONCEPT_ASSERT((VertexMutablePropertyGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((EdgeMutablePropertyGraphConcept< Graph >)); @@ -64,6 +67,7 @@ int main(int, char*[]) BOOST_CONCEPT_ASSERT((EdgeListGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((AdjacencyMatrixConcept< Graph >)); BOOST_CONCEPT_ASSERT((VertexMutablePropertyGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((EdgeMutablePropertyGraphConcept< Graph >)); diff --git a/test/cmake_install_test/CMakeLists.txt b/test/cmake_install_test/CMakeLists.txt new file mode 100644 index 00000000..36fd07c4 --- /dev/null +++ b/test/cmake_install_test/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright 2018, 2019, 2021 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.5...3.16) + +project(boost_graph_install_test LANGUAGES CXX) + +if(BOOST_RUNTIME_LINK STREQUAL "static") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +find_package(boost_graph REQUIRED) + +add_executable(main main.cpp) +target_link_libraries(main Boost::graph) + +enable_testing() +add_test(NAME main COMMAND main) + +add_custom_target(check VERBATIM COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --no-tests=error -C $) diff --git a/test/cmake_install_test/main.cpp b/test/cmake_install_test/main.cpp new file mode 100644 index 00000000..cedd2df8 --- /dev/null +++ b/test/cmake_install_test/main.cpp @@ -0,0 +1,24 @@ +// Copyright 2006 The Trustees of Indiana University. + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// Make sure adjacency_list works with EdgeListS=setS +#include + +using namespace boost; + +typedef adjacency_list< vecS, vecS, undirectedS, no_property, no_property, + no_property, setS > + GraphType; + +int main() +{ + GraphType g(10); + add_vertex(g); + add_edge(0, 5, g); +} diff --git a/test/cmake_subdir_test/CMakeLists.txt b/test/cmake_subdir_test/CMakeLists.txt new file mode 100644 index 00000000..d1ece4a6 --- /dev/null +++ b/test/cmake_subdir_test/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright 2018, 2019, 2021 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.5...3.16) + +project(boost_graph_subdir_test LANGUAGES CXX) + +if(BOOST_RUNTIME_LINK STREQUAL "static") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +# Put .dll in the same directory as .exe +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +set(BOOST_INCLUDE_LIBRARIES graph) + +add_subdirectory(../../../../ deps/boost EXCLUDE_FROM_ALL) + +add_executable(main main.cpp) +target_link_libraries(main Boost::graph) + +enable_testing() +add_test(NAME main COMMAND main) + +add_custom_target(check VERBATIM COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --no-tests=error -C $) diff --git a/test/cmake_subdir_test/main.cpp b/test/cmake_subdir_test/main.cpp new file mode 100644 index 00000000..cedd2df8 --- /dev/null +++ b/test/cmake_subdir_test/main.cpp @@ -0,0 +1,24 @@ +// Copyright 2006 The Trustees of Indiana University. + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// Make sure adjacency_list works with EdgeListS=setS +#include + +using namespace boost; + +typedef adjacency_list< vecS, vecS, undirectedS, no_property, no_property, + no_property, setS > + GraphType; + +int main() +{ + GraphType g(10); + add_vertex(g); + add_edge(0, 5, g); +} diff --git a/test/dominator_tree_test.cpp b/test/dominator_tree_test.cpp index 86835cf0..2b53b948 100644 --- a/test/dominator_tree_test.cpp +++ b/test/dominator_tree_test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include using namespace std; @@ -24,13 +25,51 @@ struct DominatorCorrectnessTestSet using namespace boost; -typedef adjacency_list< listS, listS, bidirectionalS, - property< vertex_index_t, std::size_t >, no_property > - G; - -int main(int, char*[]) +// a workaround for the C++ standard before C++17, after switching to C++17, +// the method may be just inlined into the run_test() with constexpr if. +namespace detail { - typedef DominatorCorrectnessTestSet::edge edge; + +template < typename Graph > +void index_graph(Graph&, std::true_type /*IsRandomAccessAdjacentList*/) +{ + // nothing to do for already indexed adjacent list +} + +template < typename Graph > +void index_graph(Graph& g, std::false_type /*IsRandomAccessAdjacentList*/) +{ + using IndexMap = typename property_map< Graph, vertex_index_t >::type; + IndexMap indexMap(get(vertex_index, g)); + typename graph_traits< Graph >::vertex_iterator uItr, uEnd; + int j = 0; + for (boost::tie(uItr, uEnd) = vertices(g); uItr != uEnd; ++uItr, ++j) + { + put(indexMap, *uItr, j); + } +} + +} // namespace detail + +template < typename OEL, typename VL, typename D, typename VP, typename EP, + typename GP, typename EL > +void index_graph(adjacency_list< OEL, VL, D, VP, EP, GP, EL >& g) +{ + using Traits = adjacency_list_traits< OEL, VL, D, EL >; + ::detail::index_graph( + g, std::integral_constant< bool, Traits::is_rand_access::value > {}); +} + +template < typename D, typename VP, typename EP, typename GP, typename A > +void index_graph(adjacency_matrix&) +{ + // nothing to do for already indexed adjacent matrix +} + +template < typename Graph > +void run_test() +{ + using edge = DominatorCorrectnessTestSet::edge; DominatorCorrectnessTestSet testSet[7]; @@ -217,34 +256,32 @@ int main(int, char*[]) { const int numOfVertices = testSet[i].numOfVertices; - G g(testSet[i].edges.begin(), testSet[i].edges.end(), numOfVertices); + Graph g(testSet[i].edges.begin(), testSet[i].edges.end(), numOfVertices); - typedef graph_traits< G >::vertex_descriptor Vertex; - typedef property_map< G, vertex_index_t >::type IndexMap; - typedef iterator_property_map< vector< Vertex >::iterator, IndexMap > - PredMap; + using Vertex = typename graph_traits< Graph >::vertex_descriptor; + using IndexMap = typename property_map< Graph, vertex_index_t >::type; + IndexMap indexMap(get(vertex_index, g)); + using PredMap + = iterator_property_map< typename vector< Vertex >::iterator, IndexMap >; + + index_graph(g); vector< Vertex > domTreePredVector, domTreePredVector2; - IndexMap indexMap(get(vertex_index, g)); - graph_traits< G >::vertex_iterator uItr, uEnd; - int j = 0; - for (boost::tie(uItr, uEnd) = vertices(g); uItr != uEnd; ++uItr, ++j) - { - put(indexMap, *uItr, j); - } // Lengauer-Tarjan dominator tree algorithm domTreePredVector = vector< Vertex >( - num_vertices(g), graph_traits< G >::null_vertex()); + num_vertices(g), graph_traits< Graph >::null_vertex()); PredMap domTreePredMap = make_iterator_property_map(domTreePredVector.begin(), indexMap); lengauer_tarjan_dominator_tree(g, vertex(0, g), domTreePredMap); vector< int > idom(num_vertices(g)); + typename graph_traits< Graph >::vertex_iterator uItr, uEnd; for (boost::tie(uItr, uEnd) = vertices(g); uItr != uEnd; ++uItr) { - if (get(domTreePredMap, *uItr) != graph_traits< G >::null_vertex()) + if (get(domTreePredMap, *uItr) + != graph_traits< Graph >::null_vertex()) idom[get(indexMap, *uItr)] = get(indexMap, get(domTreePredMap, *uItr)); else @@ -260,7 +297,7 @@ int main(int, char*[]) // compare results of fast version and slow version of dominator tree domTreePredVector2 = vector< Vertex >( - num_vertices(g), graph_traits< G >::null_vertex()); + num_vertices(g), graph_traits< Graph >::null_vertex()); domTreePredMap = make_iterator_property_map(domTreePredVector2.begin(), indexMap); @@ -269,7 +306,8 @@ int main(int, char*[]) vector< int > idom2(num_vertices(g)); for (boost::tie(uItr, uEnd) = vertices(g); uItr != uEnd; ++uItr) { - if (get(domTreePredMap, *uItr) != graph_traits< G >::null_vertex()) + if (get(domTreePredMap, *uItr) + != graph_traits< Graph >::null_vertex()) idom2[get(indexMap, *uItr)] = get(indexMap, get(domTreePredMap, *uItr)); else @@ -283,6 +321,21 @@ int main(int, char*[]) for (k = 0; k < num_vertices(g); ++k) BOOST_TEST(domTreePredVector[k] == domTreePredVector2[k]); } + cout << endl; +} + +int main(int, char*[]) +{ + using AdjacencyListList = adjacency_list< listS, listS, bidirectionalS, + property< vertex_index_t, std::size_t >, no_property >; + + using AdjacencyListVec = adjacency_list< listS, vecS, bidirectionalS >; + + using AdjacencyMatrix = adjacency_matrix< directedS >; + + run_test< AdjacencyListList >(); + run_test< AdjacencyListVec >(); + run_test< AdjacencyMatrix >(); return boost::report_errors(); } diff --git a/test/graph.cpp b/test/graph.cpp index 65c6cf92..614fb463 100644 --- a/test/graph.cpp +++ b/test/graph.cpp @@ -70,11 +70,8 @@ bool check_vertex_cleared(Graph& g, Vertex v, ID id) found = ai; break; } -#elif defined(BOOST_NO_CXX98_BINDERS) - found - = std::find_if(ai, aiend, std::bind(cmp, v, std::placeholders::_1)); #else - found = std::find_if(ai, aiend, std::bind1st(cmp, v)); + found = std::find_if(ai, aiend, [&v, &cmp](const auto& el) { return cmp(v, el); }); #endif if (found != aiend) diff --git a/test/graphviz_test.cpp b/test/graphviz_test.cpp index c29b05f7..a2ea6374 100644 --- a/test/graphviz_test.cpp +++ b/test/graphviz_test.cpp @@ -13,8 +13,6 @@ #define BOOST_GRAPHVIZ_USE_ISTREAM #include -#include -#include #include #include #include @@ -29,8 +27,6 @@ typedef float Mass; typedef double Weight; typedef std::map< node_t, Mass > expected_masses_t; typedef std::map< edge_t, Weight > expected_weights_t; -#define MAP_MASSES boost::assign::list_of< std::pair< node_t, Mass > > -#define MAP_WEIGHTS boost::assign::list_of< std::pair< edge_t, Weight > > struct Fixture { @@ -47,14 +43,14 @@ namespace Directed static Fixture const basic { "digraph { a node [mass = 7.7] c e [mass = 6.66] }", 3, - MAP_MASSES("a", 0.0f)("c", 7.7f)("e", 6.66f), + { { "a", 0.0f }, { "c", 7.7f }, { "e", 6.66f } }, expected_weights_t(), }; static Fixture const basic_aliased { "digraph { a node [mass = 7.7] \"a\" e [mass = 6.66] }", 2, - MAP_MASSES("a", 0.0f)("e", 6.66f), + { { "a", 0.0f }, { "e", 6.66f } }, expected_weights_t(), }; @@ -64,9 +60,11 @@ namespace Directed "d ->e->a [weight=.5]}", 6, expected_masses_t(), - MAP_WEIGHTS(edge_t("a", "b"), 0.0)(edge_t("c", "d"), 7.7)( - edge_t("e", "f"), 6.66)(edge_t("d", "e"), 0.5)( - edge_t("e", "a"), 0.5), + { + { edge_t("a", "b"), 0.0 }, { edge_t("c", "d"), 7.7 }, + { edge_t("e", "f"), 6.66 }, { edge_t("d", "e"), 0.5 }, + { edge_t("e", "a"), 0.5 } // + }, }; } @@ -75,7 +73,7 @@ namespace Undirected static Fixture const basic { "graph { a nodE [mass = 7.7] c e [mass =\\\n6.66] }", 3, - MAP_MASSES("a", 0.0f)("c", 7.7f)("e", 6.66f), + { { "a", 0.0f }, { "c", 7.7f }, { "e", 6.66f } }, expected_weights_t(), }; @@ -84,8 +82,10 @@ namespace Undirected "c -- d e -- f [weight = 6.66] }", 6, expected_masses_t(), - MAP_WEIGHTS(edge_t("a", "b"), 0.0)(edge_t("c", "d"), 7.7)( - edge_t("e", "f"), 6.66), + { + { edge_t("a", "b"), 0.0 }, { edge_t("c", "d"), 7.7 }, + { edge_t("e", "f"), 6.66 } // + }, }; } @@ -331,7 +331,7 @@ void test_parallel_edges() "diGraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }", 2, expected_masses_t(), - MAP_WEIGHTS(edge_t("a", "b"), 7.7), + { { edge_t("a", "b"), 7.7 } }, }; TEST_GRAPH(Models::DiGraph, parallel); BOOST_TEST_THROWS(TEST_GRAPH(Models::DiGraphNoParallel, parallel), @@ -345,7 +345,7 @@ void test_graph_property_test_1() "digraph { graph [name=\"foo \\\"escaped\\\"\"] a c e [mass = 6.66] " "}", 3, - MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + { { "a", 0.0f }, { "c", 0.0f }, { "e", 6.66f } }, expected_weights_t(), }; TEST_GRAPH(Models::DiGraph, named, "", "foo \"escaped\""); @@ -357,7 +357,7 @@ void test_graph_property_test_2() Fixture named { "digraph { name=\"fo\"+ \"\\\no\" a c e [mass = 6.66] }", 3, - MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + { { "a", 0.0f }, { "c", 0.0f }, { "e", 6.66f } }, expected_weights_t(), }; TEST_GRAPH(Models::DiGraph, named, "", "foo"); // SEHE why not "foo\no"? @@ -372,7 +372,7 @@ void test_graph_property_test_3() Fixture html_named { "digraph { name=" + graph_name + " a c e [mass = 6.66] }", 3, - MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + { { "a", 0.0f }, { "c", 0.0f }, { "e", 6.66f } }, expected_weights_t(), }; TEST_GRAPH(Models::DiGraph, html_named, "", graph_name); diff --git a/test/is_straight_line_draw_test.cpp b/test/is_straight_line_draw_test.cpp index 3774ed2f..17aad9c4 100644 --- a/test/is_straight_line_draw_test.cpp +++ b/test/is_straight_line_draw_test.cpp @@ -192,5 +192,32 @@ int main(int, char*[]) BOOST_TEST(!is_straight_line_drawing(g, drawing)); + + // issue #388 + g.clear(); + add_edge(0, 1, g); + add_edge(2, 0, g); + add_edge(1, 2, g); + + struct coord_t { size_t x, y; }; + std::vector coordinates{ + {4143438, 86426}, + {4064945, 7932}, + {4064944, 7931} + }; +/* + There is a very small angle between edge 0--1 and edge 2--0 at vertex 0, + with slope of edges 78494/78493 != 78495/78494, which cannot be + correctly handled by double type function "intersects()" called + by "is_straight_line_drawing()": + +4143438-4064945 = 78493 +86426-7932 = 78494 + +4143438-4064944 = 78494 +86426-7931 = 78495 +*/ + BOOST_TEST(is_straight_line_drawing(g, coordinates.data())); + return boost::report_errors(); } diff --git a/test/named_vertices_test.cpp b/test/named_vertices_test.cpp index 69ba7f18..4d13785a 100644 --- a/test/named_vertices_test.cpp +++ b/test/named_vertices_test.cpp @@ -91,5 +91,8 @@ int main(int, char*[]) BOOST_TEST(map[*find_vertex("Cincinnatti", map)].population == -1); + // Ensure copy constructor properly initializes base classes. + BOOST_TEST_NO_THROW(RoadMap{map}); + return boost::report_errors(); } diff --git a/test/reverse_graph_cc.cpp b/test/reverse_graph_cc.cpp index 7965f25c..a4e5c56c 100644 --- a/test/reverse_graph_cc.cpp +++ b/test/reverse_graph_cc.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -59,5 +60,26 @@ int main(int, char*[]) get_property(gr, graph_name_t()); set_property(gr, graph_name_t(), "foo"); } + // Check matrix + { + typedef adjacency_matrix< directedS, + property< vertex_color_t, int >, property< edge_weight_t, int >, + property< graph_name_t, std::string > > + AdjMatrix; + typedef reverse_graph< AdjMatrix > Graph; + BOOST_CONCEPT_ASSERT((VertexListGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< Graph >)); + typedef graph_traits< Graph >::vertex_descriptor Vertex; + typedef graph_traits< Graph >::edge_descriptor Edge; + BOOST_CONCEPT_ASSERT( + (ReadablePropertyGraphConcept< Graph, Vertex, vertex_color_t >)); + BOOST_CONCEPT_ASSERT( + (ReadablePropertyGraphConcept< Graph, Edge, edge_weight_t >)); + BOOST_CONCEPT_ASSERT( + (ReadablePropertyGraphConcept< Graph, Edge, edge_underlying_t >)); + AdjMatrix g(42); + Graph gr(g); + get_property(gr, graph_name_t()); + } return 0; } diff --git a/test/test_graphs.cpp b/test/test_graphs.cpp index 81c489bb..577d9fff 100644 --- a/test/test_graphs.cpp +++ b/test/test_graphs.cpp @@ -132,6 +132,8 @@ int main() Graph; BOOST_META_ASSERT(is_directed_graph< Graph >); BOOST_META_ASSERT(!is_multigraph< Graph >); + BOOST_META_ASSERT(is_bidirectional_graph< Graph >); + BOOST_META_ASSERT(is_directed_bidirectional_graph< Graph >); BOOST_META_ASSERT(has_vertex_property< Graph >); BOOST_META_ASSERT(has_bundled_vertex_property< Graph >); BOOST_META_ASSERT(has_edge_property< Graph >); @@ -145,6 +147,8 @@ int main() typedef adjacency_matrix< directedS, VertexBundle, EdgeBundle > Graph; BOOST_META_ASSERT(is_directed_graph< Graph >); BOOST_META_ASSERT(!is_multigraph< Graph >); + BOOST_META_ASSERT(is_bidirectional_graph< Graph >); + BOOST_META_ASSERT(is_directed_bidirectional_graph< Graph >); BOOST_META_ASSERT(has_vertex_property< Graph >); BOOST_META_ASSERT(has_bundled_vertex_property< Graph >); BOOST_META_ASSERT(has_edge_property< Graph >); diff --git a/test/two_graphs_common_spanning_trees_test.cpp b/test/two_graphs_common_spanning_trees_test.cpp index af641bd6..0a460948 100644 --- a/test/two_graphs_common_spanning_trees_test.cpp +++ b/test/two_graphs_common_spanning_trees_test.cpp @@ -8,17 +8,12 @@ // Efficient Algorithm for Common Spanning Tree Problem // Electron. Lett., 28 April 1983, Volume 19, Issue 9, p.346-347 -#include #include -#include #include -#include #include #include #include -using namespace boost::assign; - namespace boost { @@ -32,14 +27,8 @@ typedef boost::adjacency_list< boost::vecS, // OutEdgeList > Graph; -typedef boost::graph_traits< Graph >::vertex_descriptor vertex_descriptor; - typedef boost::graph_traits< Graph >::edge_descriptor edge_descriptor; -typedef boost::graph_traits< Graph >::vertex_iterator vertex_iterator; - -typedef boost::graph_traits< Graph >::edge_iterator edge_iterator; - template < typename Coll, typename Seq > struct check_edge { public: @@ -56,8 +45,8 @@ public: { bool found = false; - for (typename Coll::iterator iterator = coll.begin(); - !found && iterator != coll.end(); ++iterator) + for (auto iterator = coll.begin(); !found && iterator != coll.end(); + ++iterator) { Seq& coll_seq = *iterator; @@ -65,7 +54,7 @@ public: found = true; for (typename Seq::size_type pos = 0; found && pos < seq.size(); - ++pos) + ++pos) { found &= coll_seq[pos] == seq[pos]; } @@ -78,28 +67,17 @@ public: void two_graphs_common_spanning_trees_test() { Graph iG, vG; - std::vector< edge_descriptor > iG_o; - std::vector< edge_descriptor > vG_o; + std::vector< edge_descriptor > iG_o { boost::add_edge(0, 1, iG).first, + boost::add_edge(1, 3, iG).first, boost::add_edge(3, 2, iG).first, + boost::add_edge(1, 5, iG).first, boost::add_edge(5, 4, iG).first, + boost::add_edge(5, 6, iG).first, boost::add_edge(5, 3, iG).first, + boost::add_edge(3, 1, iG).first, boost::add_edge(1, 3, iG).first }; - iG_o.push_back(boost::add_edge(0, 1, iG).first); - iG_o.push_back(boost::add_edge(1, 3, iG).first); - iG_o.push_back(boost::add_edge(3, 2, iG).first); - iG_o.push_back(boost::add_edge(1, 5, iG).first); - iG_o.push_back(boost::add_edge(5, 4, iG).first); - iG_o.push_back(boost::add_edge(5, 6, iG).first); - iG_o.push_back(boost::add_edge(5, 3, iG).first); - iG_o.push_back(boost::add_edge(3, 1, iG).first); - iG_o.push_back(boost::add_edge(1, 3, iG).first); - - vG_o.push_back(boost::add_edge(0, 2, vG).first); - vG_o.push_back(boost::add_edge(0, 4, vG).first); - vG_o.push_back(boost::add_edge(0, 5, vG).first); - vG_o.push_back(boost::add_edge(5, 1, vG).first); - vG_o.push_back(boost::add_edge(5, 3, vG).first); - vG_o.push_back(boost::add_edge(5, 6, vG).first); - vG_o.push_back(boost::add_edge(5, 4, vG).first); - vG_o.push_back(boost::add_edge(5, 2, vG).first); - vG_o.push_back(boost::add_edge(2, 6, vG).first); + std::vector< edge_descriptor > vG_o { boost::add_edge(0, 2, vG).first, + boost::add_edge(0, 4, vG).first, boost::add_edge(0, 5, vG).first, + boost::add_edge(5, 1, vG).first, boost::add_edge(5, 3, vG).first, + boost::add_edge(5, 6, vG).first, boost::add_edge(5, 4, vG).first, + boost::add_edge(5, 2, vG).first, boost::add_edge(2, 6, vG).first }; std::vector< std::vector< bool > > coll; boost::tree_collector< std::vector< std::vector< bool > >, @@ -113,12 +91,10 @@ void two_graphs_common_spanning_trees_test() checker; std::vector< bool > check; - check.clear(); - check += true, true, true, true, true, true, false, false, false; + check.assign({ true, true, true, true, true, true, false, false, false }); checker(coll, check); - check.clear(); - check += true, true, true, true, true, true, false, false, false; + check.assign({ true, true, true, true, true, true, false, false, false }); checker(coll, check); } diff --git a/test/undirected_dfs_visitor.cpp b/test/undirected_dfs_visitor.cpp new file mode 100644 index 00000000..da43b295 --- /dev/null +++ b/test/undirected_dfs_visitor.cpp @@ -0,0 +1,104 @@ +//======================================================================= +// Copyright 2024 +// Author: Daniel Yang +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +//======================================================================= + +#include +#include + +#include +#include + +#include +#include + +using Graph = boost::adjacency_list< + boost::vecS, + boost::vecS, + boost::undirectedS, + boost::no_property, + boost::property>; + +using Vertex = boost::graph_traits::vertex_descriptor; +using Edge = boost::graph_traits::edge_descriptor; + +struct DFSVisitorLogger : boost::default_dfs_visitor { + std::vector &log; + + DFSVisitorLogger(std::vector &log) : log(log) {} + + void log_vertex(const Vertex v, const std::string &event) { + log.push_back("vertex " + std::to_string(v) + " " + event); + } + void log_edge(const Edge e, const std::string &event, const Graph &g) { + log.push_back("edge (" + std::to_string(boost::source(e, g)) + "," + std::to_string(boost::target(e, g)) + ") " + event); + } + + void discover_vertex(Vertex v, const Graph &g) { + log_vertex(v, "discovered"); + } + void finish_vertex(Vertex v, const Graph &g) { + log_vertex(v, "finished"); + } + void examine_edge(Edge e, const Graph &g) { + log_edge(e, "examined", g); + } + void tree_edge(Edge e, const Graph &g) { + log_edge(e, "tree", g); + } + void back_edge(Edge e, const Graph &g) { + log_edge(e, "back", g); + } + void forward_or_cross_edge(Edge e, const Graph &g) { + log_edge(e, "forward_cross", g); + } + void finish_edge(Edge e, const Graph &g) { + log_edge(e, "finished", g); + } +}; + +int main() { + Graph g(3); + boost::add_edge(0, 1, g); + boost::add_edge(1, 2, g); + + std::vector expected_answer = { + "vertex 0 discovered", + "edge (0,1) examined", + "edge (0,1) tree", + "vertex 1 discovered", + "edge (1,0) examined", + "edge (1,0) finished", + "edge (1,2) examined", + "edge (1,2) tree", + "vertex 2 discovered", + "edge (2,1) examined", + "edge (2,1) finished", + "vertex 2 finished", + "edge (1,2) finished", + "vertex 1 finished", + "edge (0,1) finished", + "vertex 0 finished", + }; + std::vector actual_answer; + + // run undirected_dfs + DFSVisitorLogger dfs_visitor_logger(actual_answer); + boost::undirected_dfs(g, + boost::visitor(dfs_visitor_logger) + .edge_color_map(boost::get(boost::edge_color, g))); + + // check if all vertices and edges have been visited in the correct order + BOOST_TEST(expected_answer.size() == actual_answer.size()); + if (expected_answer.size() == actual_answer.size()) { + for (int i = 0; i < expected_answer.size(); ++i) { + BOOST_TEST(expected_answer[i] == actual_answer[i]); + } + } + + return boost::report_errors(); +} diff --git a/test/weighted_matching_test.cpp b/test/weighted_matching_test.cpp index 2baa0adc..30042c55 100644 --- a/test/weighted_matching_test.cpp +++ b/test/weighted_matching_test.cpp @@ -139,6 +139,7 @@ Graph make_graph(typename graph_traits< Graph >::vertices_size_type num_v, int main(int, char*[]) { std::ifstream in_file("weighted_matching.dat"); + BOOST_TEST(in_file.good()); std::string line; while (std::getline(in_file, line)) { diff --git a/test/weighted_matching_test2.cpp b/test/weighted_matching_test2.cpp new file mode 100644 index 00000000..3bdacdee --- /dev/null +++ b/test/weighted_matching_test2.cpp @@ -0,0 +1,448 @@ +//======================================================================= +// Copyright (c) 2024 Joris van Rantwijk +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +//======================================================================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; + + +template +using adj_vec_test_graph = adjacency_list< + vecS, // OutEdgeList + vecS, // VertexList + undirectedS, // DirectedS + no_property, // VertexProperty (vertex_index is implicit) + property>; // EdgeProperty + +template +using adj_list_test_graph = adjacency_list< + listS, // OutEdgeList + listS, // VertexList + undirectedS, // DirectedS + property, // VertexProperty + property>; // EdgeProperty + +template +using adj_matrix_test_graph = adjacency_matrix< + undirectedS, // Directed + no_property, // VertexProperty (vertex_index is implicit) + property>; // EdgeProperty + + +template +struct edge_info +{ + unsigned int x, y; + WeightType w; +}; + + +// Initialize vertex_index if necessary. +template +struct vertex_index_installer +{ + static void install(Graph&) {} +}; + +template +struct vertex_index_installer> +{ + static void install(adj_list_test_graph& g) + { + auto vrange = vertices(g); + unsigned int i = 0; + for (auto it = vrange.first; it != vrange.second; ++it, ++i) + put(vertex_index, g, *it, i); + } +}; + + +template +Graph make_graph( + unsigned int nv, + const std::vector>& edges_info) +{ + using EdgeProperty = property; + Graph g(nv); + vertex_index_installer::install(g); + for (const auto& e : edges_info) + add_edge(vertex(e.x, g), vertex(e.y, g), EdgeProperty(e.w), g); + return g; +} + + +template +using graph_edge_weight_type = typename property_traits< + typename property_map::type>::value_type; + + +template +std::pair, bool> +check_matching(const Graph& g, const MateMap& mate) +{ + graph_edge_weight_type matching_weight = 0; + unsigned int n_matched_edges = 0; + + auto edge_range = edges(g); + for (auto it = edge_range.first; it != edge_range.second; ++it) + { + if (mate[source(*it, g)] == target(*it, g)) + { + ++n_matched_edges; + matching_weight += get(edge_weight, g, *it); + } + } + + unsigned int n_matched_vertices = 0; + auto vertex_range = vertices(g); + for (auto it = vertex_range.first; it != vertex_range.second; ++it) + { + if (mate[*it] != graph_traits::null_vertex()) + { + if (mate[mate[*it]] != *it) + return std::make_pair(0, false); + ++n_matched_vertices; + } + } + + if (n_matched_vertices != 2 * n_matched_edges) + return std::make_pair(0, false); + + return std::make_pair(matching_weight, true); +} + + +template +std::string show_graph(const Graph& g) +{ + std::ostringstream ss; + auto edge_range = edges(g); + for (auto it = edge_range.first; it != edge_range.second; ++it) + { + unsigned int x = get(vertex_index, g, source(*it, g)); + unsigned int y = get(vertex_index, g, target(*it, g)); + auto w = get(edge_weight, g, *it); + if (ss.tellp() > 0) + ss << " "; + ss << "{" << x << "," << y << "," << w << "}"; + } + return ss.str(); +} + + +template +void test_matching(const Graph& g, WeightType answer) +{ + using vertex_index_map_t = + typename property_map::type; + using mate_t = vector_property_map< + typename graph_traits::vertex_descriptor, vertex_index_map_t>; + + mate_t mate(num_vertices(g)); + maximum_weighted_matching(g, mate); + + WeightType weight; + bool consistent_matching; + std::tie(weight, consistent_matching) = check_matching(g, mate); + BOOST_TEST(consistent_matching); + if (! consistent_matching) + { + std::cout << std::endl + << "Inconsistent answer for graph" << std::endl + << " " << show_graph(g) << std::endl; + } + else + { + bool same_answer; + if (std::numeric_limits::is_integer) + { + same_answer = (weight == answer); + } + else + { + WeightType max_error = std::max(1e-14, answer * 1e-14); + same_answer = (std::abs(weight - answer) <= max_error); + } + + BOOST_TEST(same_answer); + if (! same_answer) + { + std::cout << std::endl + << "Wrong answer for graph" << std::endl + << " " << show_graph(g) << std::endl + << " found weight " << weight + << " while expecting " << answer << std::endl; + } + } +} + + +template +void run_test_graph( + unsigned int nv, + const std::vector>& edges_info, + WeightType answer) +{ + using vec_graph_t = adj_vec_test_graph; + test_matching( + make_graph(nv, edges_info), answer); + + using list_graph_t = adj_list_test_graph; + test_matching( + make_graph(nv, edges_info), answer); + + using matrix_graph_t = adj_matrix_test_graph; + test_matching( + make_graph(nv, edges_info), answer); +} + + +int main(int, char*[]) +{ + // Simple test cases: + run_test_graph(0, {}, 0); + run_test_graph(1, {}, 0); + run_test_graph(2, {}, 0); + run_test_graph(2, {{0, 1, 1}}, 1); + run_test_graph(3, {{0, 1, 10}, {1, 2, 11}}, 11); + run_test_graph(4, {{0, 1, 5}, {1, 2, 11}, {2, 3, 5}}, 11); + run_test_graph(4, {{0, 1, 5}, {1, 2, 11}, {2, 3, 7}}, 12); + + // Floating point edge weights: + run_test_graph(4, { + {0, 1, 3.1415}, {1, 2, 2.7183}, {0, 2, 3.0}, {0, 3, 1.4142}}, + 4.1325); + + // Negative edge weights: + run_test_graph(4, { + {0, 1, 2}, {0, 2, -2}, {1, 2, 1}, {1, 3, -1}, {2, 3, -6}}, + 2); + + // Blossoms: + run_test_graph(4, {{0, 1, 8}, {0, 2, 9}, {1, 2, 10}, {2, 3, 7}}, 15); + + run_test_graph(6, { + {0, 1, 8}, {0, 2, 9}, {1, 2, 10}, {2, 3, 7}, {0, 5, 5}, {3, 4, 6}}, + 21); + + run_test_graph(6, { + {0, 1, 9}, {0, 2, 8}, {1, 2, 10}, {0, 3, 5}, {3, 4, 4}, {0, 5, 3}}, + 17); + + run_test_graph(6, { + {0, 1, 9}, {0, 2, 8}, {1, 2, 10}, {0, 3, 5}, {3, 4, 3}, {0, 5, 4}}, + 17); + + run_test_graph(6, { + {0, 1, 9}, {0, 2, 8}, {1, 2, 10}, {0, 3, 5}, {3, 4, 3}, {2, 5, 4}}, + 16); + + run_test_graph(6, { + {0, 1, 9}, {0, 2, 9}, {1, 2, 10}, {1, 3, 8}, {2, 4, 8}, + {3, 4, 10}, {4, 5, 6}}, + 23); + + run_test_graph(8, { + {0, 1, 10}, {0, 6, 10}, {1, 2, 12}, {2, 3, 20}, {2, 4, 20}, + {3, 4, 25}, {4, 5, 10}, {5, 6, 10}, {6, 7, 8}}, + 48); + + run_test_graph(8, { + {0, 1, 8}, {0, 2, 8}, {1, 2, 10}, {1, 3, 12}, {2, 4, 12}, + {3, 4, 14}, {3, 5, 12}, {4, 6, 12}, {5, 6, 14}, {6, 7, 12}}, + 44); + + run_test_graph(8, { + {0, 1, 23}, {0, 4, 22}, {0, 5, 15}, {1, 2, 25}, {2, 3, 22}, + {3, 4, 25}, {3, 7, 14}, {4, 6, 13}}, + 67); + + run_test_graph(8, { + {0, 1, 19}, {0, 2, 20}, {0, 7, 8}, {1, 2, 25}, {1, 3, 18}, + {2, 4, 18}, {3, 4, 13}, {3, 6, 7}, {4, 5, 7}}, + 47); + + // Somewhat tricky test cases: + run_test_graph(10, { + {0, 1, 45}, {0, 4, 45}, {1, 2, 50}, {2, 3, 45}, {3, 4, 50}, + {0, 5, 30}, {2, 8, 35}, {3, 7, 35}, {4, 6, 26}, {8, 9, 5}}, + 146); + + run_test_graph(10, { + {0, 1, 45}, {0, 4, 45}, {1, 2, 50}, {2, 3, 45}, {3, 4, 50}, + {0, 5, 30}, {2, 8, 35}, {3, 7, 26}, {4, 6, 40}, {8, 9, 5}}, + 151); + + run_test_graph(10, { + {0, 1, 45}, {0, 4, 45}, {1, 2, 50}, {2, 3, 45}, {3, 4, 50}, + {0, 5, 30}, {2, 8, 35}, {3, 7, 28}, {4, 6, 26}, {8, 9, 5}}, + 139); + + run_test_graph(12, { + {0, 1, 45}, {0, 6, 45}, {1, 2, 50}, {2, 3, 45}, {3, 4, 95}, + {3, 5, 94}, {4, 5, 94}, {5, 6, 50}, {0, 7, 30}, {2, 10, 35}, + {4, 8, 36}, {6, 9, 26}, {10, 11, 5}}, + 241); + + run_test_graph(10, { + {0, 1, 40}, {0, 2, 40}, {1, 2, 60}, {1, 3, 55}, {2, 4, 55}, + {3, 4, 50}, {0, 7, 15}, {4, 6, 30}, {6, 5, 10}, {7, 9, 10}, + {3, 8, 30}}, + 145); + + run_test_graph(6, { + {0, 1, 2}, {0, 4, 3}, {1, 2, 7}, {1, 5, 2}, {2, 3, 9}, + {2, 5, 4}, {3, 4, 8}, {3, 5, 4}}, + 15); + + run_test_graph(6, { + {0, 1, 8}, {0, 2, 8}, {1, 2, 9}, {1, 3, 6}, {2, 4, 7}, + {3, 4, 8}, {3, 5, 5}}, + 20); + + run_test_graph(7, { + {0, 1, 7}, {0, 2, 7}, {1, 2, 9}, {0, 3, 7}, {0, 4, 7}, + {3, 4, 9}, {5, 6, 2}}, + 20); + + run_test_graph(7, { + {0, 1, 7}, {0, 4, 6}, {1, 2, 9}, {2, 3, 8}, {3, 4, 9}, + {3, 5, 1}, {4, 5, 1}}, + 18); + + run_test_graph(5, { + {0, 2, 7}, {0, 3, 3}, {1, 3, 1}, {2, 3, 5}, {2, 4, 5}}, + 8); + + run_test_graph(7, { + {0, 1, 15}, {0, 2, 10}, {0, 3, 11}, {0, 5, 17}, {1, 4, 12}, + {1, 5, 8}, {4, 6, 15}, {5, 6, 7}}, + 34); + + run_test_graph(7, { + {0, 1, 12}, {0, 3, 11}, {0, 4, 11}, {1, 3, 12}, {1, 5, 11}, + {2, 3, 14}, {2, 6, 14}, {3, 6, 11}}, + 37); + + run_test_graph(7, { + {0, 1, 19}, {0, 3, 17}, {0, 4, 19}, {1, 2, 15}, {1, 4, 21}, + {3, 5, 18}, {3, 6, 11}, {4, 5, 19}}, + 52); + + run_test_graph(6, { + {0, 1, 19}, {0, 2, 19}, {0, 3, 15}, {0, 4, 17}, {1, 2, 21}, + {3, 4, 16}, {4, 5, 10}}, + 46); + + run_test_graph(6, { + {0, 1, 48}, {0, 3, 42}, {0, 4, 57}, {1, 3, 51}, {1, 5, 36}, + {2, 3, 23}, {4, 5, 46}}, + 117); + + run_test_graph(6, { + {0, 2, 7}, {0, 5, 20}, {1, 2, 50}, {1, 4, 46}, {2, 3, 35}, + {2, 4, 8}, {2, 5, 25}, {3, 5, 47}}, + 101); + + run_test_graph(5, { + {0, 1, 48}, {0, 2, 44}, {0, 4, 48}, {1, 4, 36}, {3, 4, 31}}, + 80); + + run_test_graph(6, { + {3, 5, 71}, {2, 4, 36}, {3, 4, 27}, {5, 1, 29}, {2, 0, 48}, + {1, 0, 60}, {1, 3, 11}, {4, 5, 54}}, + 167); + + run_test_graph(6, { + {0, 2, 92}, {5, 4, 50}, {5, 0, 86}, {2, 1, 87}, {1, 3, 39}, + {4, 0, 2}, {4, 1, 83}, {0, 1, 56}}, + 181); + + // Triangles, unit weight: + run_test_graph(9, { + {0, 1, 1}, {0, 2, 1}, {1, 2, 1}, {0, 3, 1}, + {3, 4, 1}, {3, 5, 1}, {4, 5, 1}, {4, 7, 1}, + {6, 7, 1}, {6, 8, 1}, {7, 8, 1}}, + 4); + + // Trigger known bugs in a previous version of maximum_weighted_matching: + + // wrong answer + run_test_graph(8, { + {4, 7, 453}, {0, 4, 627}, {4, 6, 853}, {6, 7, 344}, {5, 6, 906}, + {4, 5, 689}, {2, 3, 741}, {2, 7, 746}, {3, 6, 647}, {1, 5, 385}, + {5, 7, 215}, {3, 7, 640}}, + 2405); + + // wrong answer + run_test_graph(12, { + {0, 3, 448}, {0, 4, 919}, {0, 5, 918}, {0, 9, 72}, {1, 3, 830}, + {1, 4, 687}, {1, 5, 559}, {1, 6, 580}, {1, 7, 679}, {1, 8, 627}, + {2, 4, 585}, {2, 5, 835}, {2, 6, 822}, {2, 8, 462}, {2, 11, 380}, + {3, 4, 328}, {3, 5, 860}, {3, 7, 297}, {3, 8, 590}, {3, 11, 235}, + {4, 6, 692}, {4, 10, 227}, {4, 11, 354}, {5, 6, 160}, {5, 9, 400}, + {6, 8, 410}, {6, 9, 420}, {7, 11, 924}, {9, 11, 825}, {10, 11, 790}}, + 4233); + + // assertion + run_test_graph(9, { + {4, 6, 796}, {3, 4, 553}, {0, 1, 792}, {1, 7, 1000}, {4, 5, 360}, + {5, 7, 183}, {4, 8, 694}, {0, 4, 741}, {0, 2, 483}, {5, 8, 228}, + {7, 8, 644}, {1, 3, 236}, {6, 7, 895}, {1, 6, 913}, {1, 8, 617}}, + 2593); + + // assertion + run_test_graph(10, { + {0, 6, 892}, {2, 4, 517}, {4, 7, 560}, {1, 5, 828}, {1, 7, 831}, + {6, 7, 397}, {4, 5, 43}, {5, 6, 944}, {6, 9, 215}, {5, 7, 753}, + {4, 6, 901}, {1, 2, 530}, {2, 8, 384}, {3, 4, 499}, {5, 8, 190}}, + 2674); + + // segmentation fault + run_test_graph(7, { + {5, 6, 799}, {0, 4, 601}, {0, 1, 578}, {0, 3, 373}, {4, 6, 675}, + {4, 5, 925}, {0, 5, 697}, {3, 4, 260}, {1, 3, 464}, {1, 5, 845}, + {0, 2, 176}, {2, 5, 685}}, + 1938); + + // segmentation fault + run_test_graph(8, { + {2, 7, 420}, {6, 7, 414}, {2, 3, 421}, {2, 6, 854}, {1, 6, 997}, + {4, 5, 46}, {1, 2, 467}, {2, 4, 230}, {3, 7, 555}, {0, 3, 334}, + {0, 7, 341}, {0, 6, 634}}, + 1805); + + // hang + run_test_graph(7, { + {2, 3, 837}, {1, 4, 458}, {3, 4, 291}, {5, 6, 601}, {1, 2, 202}, + {0, 4, 491}, {4, 5, 910}, {0, 1, 159}, {3, 5, 684}, {4, 6, 139}, + {0, 2, 792}, {1, 3, 232}}, + 1934); + + // hang + run_test_graph(12, { + {0, 1, 856}, {0, 4, 462}, {0, 6, 874}, {0, 7, 406}, {0, 10, 294}, + {1, 3, 936}, {1, 5, 852}, {1, 7, 501}, {1, 8, 555}, {1, 10, 41}, + {2, 3, 325}, {2, 6, 748}, {2, 9, 808}, {3, 5, 870}, {3, 7, 25}, + {3, 8, 663}, {3, 9, 897}, {4, 7, 617}, {4, 9, 435}, {4, 10, 818}, + {4, 11, 933}, {5, 6, 608}, {5, 8, 636}, {6, 9, 274}, {6, 10, 279}, + {7, 9, 705}, {7, 10, 114}, {8, 11, 602}, {9, 10, 764}, {10, 11, 413}}, + 4599); + + return boost::report_errors(); +}