From a29542f89fa7b374f3e984ff48a4827408b0a7c1 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 30 Apr 2026 11:16:08 -0400 Subject: [PATCH 01/20] docs: add Graph I/O and generators entries to CHANGELOG --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d743c1..ebc4272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,22 @@ - `is_sparse_vertex_container_v` trait for compile-time graph type dispatch - Map-based graph test fixtures (`map_graph_fixtures.hpp`) with sparse vertex IDs - 456 new algorithm tests for sparse graph types (4343 → 4799) +- **Graph I/O** (``) — read/write support for three formats: + - `write_dot(os, g)` / `write_dot(os, g, vertex_attr_fn, edge_attr_fn)` — DOT/GraphViz writer; auto-formats vertex/edge values when `std::formattable`, emits user-supplied attribute strings otherwise (`io/dot.hpp`) + - `read_dot(is) -> dot_graph` — DOT parser returning a lightweight `dot_graph` structure with string-keyed vertex/edge attributes (`io/dot.hpp`) + - `write_graphml(os, g)` / `write_graphml(os, g, vertex_properties_fn, edge_properties_fn)` — GraphML (XML) writer with optional property key declarations (`io/graphml.hpp`) + - `read_graphml(is) -> graphml_graph` — GraphML parser returning a `graphml_graph` structure with per-vertex/per-edge attribute maps (`io/graphml.hpp`) + - `write_json(os, g, indent = 2)` / `write_json(os, g, vertex_attr_fn, edge_attr_fn, indent)` — JSON writer; `indent=0` for compact single-line output (`io/json.hpp`) + - `read_json(is) -> json_graph` — JSON parser returning a `json_graph` structure with string-valued attribute maps (`io/json.hpp`) + - 19 I/O tests covering write, read, and roundtrip for all three formats +- **Graph generators** (``) — produce `edge_list` sorted by source, loadable into any container via `load_edges()`: + - `erdos_renyi(n, p, seed, dist)` — G(n,p) random directed graph using the Batagelj–Brandes geometric-skip algorithm; O(E) time (`generators/erdos_renyi.hpp`) + - `grid_2d(rows, cols, seed, dist)` — bidirectional 4-connected grid graph; E/V ≈ 4 (`generators/grid.hpp`) + - `barabasi_albert(n, m, seed, dist)` — preferential-attachment scale-free graph; E/V ≈ 2m (`generators/barabasi_albert.hpp`) + - `path_graph(n, seed, dist)` — directed path 0 → 1 → … → n−1; E = n−1 (`generators/path.hpp`) + - `weight_dist` enum (`uniform` U[1,100], `exponential` Exp(0.1)+1, `constant_one`) — passed to all generators (`generators/common.hpp`) + - `edge_list` / `edge_entry` type aliases; all generators accept a `VId` template parameter (default `uint32_t`) for large graphs (`generators/common.hpp`) + - 6 generator tests covering basic properties, weight distributions, and `uint64_t` vertex IDs ### Changed - **`edge_descriptor` simplified to iterator-only storage** — removed the `conditional_t` dual-storage path; edges always store the iterator directly since edges always have physical containers. Eliminates 38 `if constexpr` branches across 6 files (~500 lines removed). From dd3b329268ef598d6179a40001464197687f7daa Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 30 Apr 2026 11:50:16 -0400 Subject: [PATCH 02/20] feat: add AdaptingThirdPartyGraph example and extend num_vertices CPO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add examples/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp: graph-v3 refactor of the v2 example; adapts MyGraph via four ADL CPOs (vertices, edges, target_id, vertex_value) with no num_vertices override - Add examples/CMakeLists.txt: adapting_third_party_graph build target - Extend num_vertices(g) CPO with a tier-3 fallback: std::ranges::size(vertices(g)) fires before std::ranges::size(g), so any graph providing vertices(g) → sized_range gets num_vertices free - Update docs to reflect the new four-tier resolution order: cpo-reference.md, contributing/cpo-implementation.md, user-guide/adjacency-lists.md - Stage AdaptingThirdPartyGraph/ (root, v2 source) and PageRank/ files --- AdaptingThirdPartyGraph/CMakeLists.txt | 12 ++ .../adapting_a_third_party_graph.cpp | 83 ++++++++++ PageRank/pagerank.hpp | 122 ++++++++++++++ PageRank/pagerank_tests.cpp | 56 +++++++ docs/contributing/cpo-implementation.md | 2 +- docs/reference/cpo-reference.md | 20 ++- docs/user-guide/adjacency-lists.md | 2 + .../adapting_a_third_party_graph.cpp | 154 ++++++++++++++++++ examples/CMakeLists.txt | 4 + include/graph/adj_list/detail/graph_cpo.hpp | 37 ++++- 10 files changed, 478 insertions(+), 14 deletions(-) create mode 100644 AdaptingThirdPartyGraph/CMakeLists.txt create mode 100644 AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp create mode 100644 PageRank/pagerank.hpp create mode 100644 PageRank/pagerank_tests.cpp create mode 100644 examples/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp diff --git a/AdaptingThirdPartyGraph/CMakeLists.txt b/AdaptingThirdPartyGraph/CMakeLists.txt new file mode 100644 index 0000000..5425e38 --- /dev/null +++ b/AdaptingThirdPartyGraph/CMakeLists.txt @@ -0,0 +1,12 @@ +# example/AdaptingThirdPartyGraph/CMakeLists.txt + +set(EXAMPLE_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/output/") +add_compile_definitions(EXAMPLE_OUTPUT_DIR="${EXAMPLE_OUTPUT_DIR}") + +#add_library(catch_main STATIC catch_main.cpp) +#target_link_libraries(catch_main PUBLIC Catch2::Catch2) +#target_link_libraries(catch_main PRIVATE project_options) +#target_link_options(catch_main INTERFACE $<$:-pthread -fconcepts-diagnostics-depth=1>) + +add_executable(AdaptingThirdPartyGraph "adapting_a_third_party_graph.cpp") +target_link_libraries(AdaptingThirdPartyGraph PRIVATE project_warnings project_options catch_main Catch2::Catch2WithMain graph) diff --git a/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp b/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp new file mode 100644 index 0000000..f397722 --- /dev/null +++ b/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2025 Andrzej Krzemienski. +// +// 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) +// +// This test file demonstrates how one can adapt one's own graph container for use with +// this Graph Library + +#include +#include +#include +#include +#include + + +namespace MyLibrary { // custom graph container, conceptually an adjacency list + +struct MyEdge { + std::string content; + int indexOfTarget; +}; + +struct MyVertex { + std::string content; + std::vector outEdges; +}; + +class MyGraph { + std::vector _vertices; + +public: + MyVertex const* getVertexByIndex(int index) const { return &_vertices[static_cast(index)]; } + + std::vector const& getAllVertices() const // !! one of customization points + { + return _vertices; + } // forced me to add this fun + + void setTopology(std::vector t) { _vertices = std::move(t); } +}; + +} // namespace MyLibrary + +namespace MyLibrary { // customization for graph, unintrusive + // although forcing me to provide `vertices()` is superfluous + +auto vertices(MyGraph const& g) { return std::views::all(g.getAllVertices()); } // for vertex_range_t + +auto edges(MyGraph const&, const MyLibrary::MyVertex& v) { return std::views::all(v.outEdges); } + +auto edges(MyGraph const& g, int i) { return edges(g, *g.getVertexByIndex(i)); } + +int vertex_id(MyGraph const& g, std::vector::const_iterator it) { + return static_cast(std::distance(g.getAllVertices().begin(), it)); +} + +int target_id(MyGraph const&, MyEdge const& uv) { return uv.indexOfTarget; } + +} // namespace MyLibrary + + +int main() { + static_assert(graph::adjacency_list); + + const MyLibrary::MyGraph g = [] { // populate the graph + MyLibrary::MyGraph r; + std::vector topo{ + // A | + /*0*/ {"A", {{"", 1}, {"", 2}}}, // / \ | + /*1*/ {"B", {{"", 3}}}, // B C | + /*2*/ {"C", {{"", 3}}}, // \ / | + /*3*/ {"D", {}} // D | + }; + r.setTopology(std::move(topo)); + return r; + }(); + + for (auto const& [vid, v] : graph::views::vertices_depth_first_search(g, 0)) + std::cout << v.content << " "; + + std::cout << std::endl; +} \ No newline at end of file diff --git a/PageRank/pagerank.hpp b/PageRank/pagerank.hpp new file mode 100644 index 0000000..83ff96a --- /dev/null +++ b/PageRank/pagerank.hpp @@ -0,0 +1,122 @@ +/** + * @file pagerank.hpp + * + * @brief PageRank (PR) ranking algorithm. + * + * @copyright Copyright (c) 2022 + * + * SPDX-License-Identifier: BSL-1.0 + * + * @authors + * Muhammad Osama + */ + +#ifndef GRAPH_PAGERANK_HPP +#define GRAPH_PAGERANK_HPP + +#include "graph/graph.hpp" +#include "graph/views/incidence.hpp" +#include "graph/views/edgelist.hpp" + +#include +#include +#include + +namespace graph { + +/** + * @brief Requirements for an edge value function: evf(uv) -> value. + * + * @tparam G Graph type. + * @tparam F Function type. +*/ +template +concept edge_weight_function = // e.g. weight(uv) + copy_constructible && is_arithmetic_v>>; + + +/** + * @brief PageRank (PR) algorithm. + * + * @tparam G The graph type. + * @tparam PageRank The ranks range type. + * @tparam EVF The edge value function that returns the weight of an edge. + * + * @param g The adjacency graph. + * @param scores [out] The page rank of each vertex in the graph, accessible through scores[uid], where uid is the vertex_id. The caller must assure size(scores) >= size(vertices(g)). + * @param damping_factor The alpha/damping factor (default = 0.85.) + * @param threshold The error threshold for convergence (default = 1e-4.) + * @param max_iterations Maximum number of iterations for convergence (default = std::numeric_limits::max().) + * @param weight_fn The edge value function (default returns 1 for each edge value.) + */ +template (edge_reference_t)>> +requires ranges::random_access_range> && integral> && + is_arithmetic_v> && edge_weight_function +void pagerank( + G&& g, // graph + PageRank& scores, // out: page rank scores + const double damping_factor = 0.85, + const double threshold = 1e-4, + const std::size_t max_iterations = std::numeric_limits::max(), + EVF weight_fn = [](edge_reference_t uv) { return ranges::range_value_t(1); }) { + using id_type = vertex_id_t; + using weight_type = ranges::range_value_t; + + std::vector plast(size(vertices(g))); + std::vector degrees(size(vertices(g))); + + // alpha * 1 / (sum of outgoing weights) -- used to determine + // out of mass spread from src to dst + std::vector iweights(size(vertices(g))); + + // Initialize the data, pagerank as 1/n_vertices. + std::ranges::fill(scores, 1.0 / double(size(vertices(g)))); + + for (auto&& [uid, u] : views::vertexlist(g)) { + // Calculate the degree of each vertex. + size_t edge_cnt = 0; + for (auto&& uv : edges(g, u)) { + ++edge_cnt; + } + degrees[uid] = static_cast(edge_cnt); + + // Find the sum of outgoing weights. + weight_type val = 0; + for (auto&& [vid, uv, w] : views::incidence(g, uid, weight_fn)) { + val += w; + } + iweights[uid] = (val != 0) ? damping_factor / val : 0; + } + + size_t iter; + for (iter = 0; iter < max_iterations; ++iter) { + // Make a copy of pagerank from previous iteration. + std::ranges::copy(scores.begin(), scores.end(), plast.begin()); + + // Handle "dangling nodes" (nodes w/ zero outdegree) + // could skip this if no nodes have sero outdegree + weight_type dsum = 0.0f; + for (auto&& [uid, u] : views::vertexlist(g)) { + dsum += (iweights[uid] == 0) ? damping_factor * scores[uid] : 0; + } + + std::ranges::fill(scores, (1 - damping_factor + dsum) / double(size(vertices(g)))); + + double error = 0; + for (auto&& [uid, vid, uv, val] : views::edgelist(g, weight_fn)) { + weight_type update = plast[uid] * iweights[uid] * val; + scores[vid] += update; + error += fabs(scores[uid] - plast[uid]); + } + + // Check for convergence + if (error < threshold) + break; + } +} + +} // namespace graph + +#endif // GRAPH_PAGERANK_HPP diff --git a/PageRank/pagerank_tests.cpp b/PageRank/pagerank_tests.cpp new file mode 100644 index 0000000..20a7932 --- /dev/null +++ b/PageRank/pagerank_tests.cpp @@ -0,0 +1,56 @@ +#include +#include +#include "csv_routes.hpp" +#include "graph/graph.hpp" +#include "graph/container/dynamic_graph.hpp" +#include "graph/algorithm/pagerank.hpp" +#include "graph/views/incidence.hpp" +#include "graph/views/edgelist.hpp" +#include +#ifdef _MSC_VER +# include "Windows.h" +#endif + +#define TEST_OPTION_OUTPUT (1) // output tests for visual inspection +#define TEST_OPTION_GEN (2) // generate unit test code to be pasted into this file +#define TEST_OPTION_TEST (3) // run unit tests +#define TEST_OPTION TEST_OPTION_OUTPUT + +using std::cout; +using std::endl; + +using graph::vertex_t; +using graph::vertex_id_t; +using graph::vertex_reference_t; +using graph::vertex_iterator_t; +using graph::vertex_edge_range_t; +using graph::edge_t; + +using graph::vertices; +using graph::edges; +using graph::vertex_value; +using graph::target_id; +using graph::target; +using graph::edge_value; +using graph::find_vertex; +using graph::vertex_id; + +using routes_volf_graph_traits = graph::container::vofl_graph_traits; +using routes_volf_graph_type = graph::container::dynamic_adjacency_graph; + +TEST_CASE("PageRank", "[pagerank]") { + init_console(); + using G = routes_volf_graph_type; + auto&& g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); + + std::vector page_rank(size(vertices(g))); + graph::pagerank(g, page_rank, 0.85, 1e-4, 10); + + std::vector answer = {0.051086017487729, 0.065561667371485, 0.106818581147795, 0.141889899564636, + 0.065561667371485, 0.078952299317762, 0.065561667371485, 0.078952299317762, + 0.260972178563747, 0.084643725419772}; + + for (auto&& [uid, u] : graph::views::vertexlist(g)) { + REQUIRE(page_rank[uid] == Approx(answer[uid]).epsilon(1e-4)); + } +} \ No newline at end of file diff --git a/docs/contributing/cpo-implementation.md b/docs/contributing/cpo-implementation.md index 9e65ad7..2981278 100644 --- a/docs/contributing/cpo-implementation.md +++ b/docs/contributing/cpo-implementation.md @@ -568,7 +568,7 @@ The following CPOs are implemented (or planned) for the Graph Container Interfac | `target_id(g, uv)` | `(G&, edge_t)` | `vertex_id_t` | **yes** | pattern extraction (integral/pair/tuple) | | `source_id(g, uv)` | `(G&, edge_t)` | `vertex_id_t` | **yes** | descriptor source | | `find_vertex(g, uid)` | `(G&, vertex_id_t)` | `vertex_iterator_t` | no | `begin(vertices(g)) + uid` | -| `num_vertices(g)` | `(const G&)` | `integral` | no | `ranges::size(vertices(g))` | +| `num_vertices(g)` | `(const G&)` | `integral` | no | `ranges::size(vertices(g))` (4-tier: member → ADL → `size(vertices(g))` → `size(g)`) | | `num_edges(g)` | `(const G&)` | `integral` | no | sum of `distance(edges(g,u))` | | `degree(g, u)` | `(const G&, vertex_t)` | `integral` | no | `ranges::size(edges(g,u))` | | `target(g, uv)` | `(G&, edge_t)` | `vertex_t` | no | `*find_vertex(g, target_id(g,uv))` | diff --git a/docs/reference/cpo-reference.md b/docs/reference/cpo-reference.md index 5964e09..d5dc768 100644 --- a/docs/reference/cpo-reference.md +++ b/docs/reference/cpo-reference.md @@ -142,6 +142,15 @@ Returns the number of vertices. | **Default** | `ranges::size(vertices(g))` | | **Complexity** | O(1) | +**Resolution order for `num_vertices(g)`:** + +1. `g.num_vertices()` — member function +2. ADL `num_vertices(g)` — free function in graph's namespace +3. `std::ranges::size(vertices(g))` — size of the vertex range (works for any graph that provides `vertices(g)` returning a `sized_range`) +4. `std::ranges::size(g)` — graph is itself a `std::ranges::sized_range` + +Most graphs need no override: tier 3 fires automatically whenever `vertices(g)` is defined and returns a sized range (e.g. `vertex_descriptor_view` over a `std::vector`). + --- ## Edge CPOs @@ -439,15 +448,18 @@ Returns the number of partitions. ## Resolution Order -Every CPO follows the same three-step customization point protocol: +Most CPOs follow a three-step customization point protocol: 1. **Member function**: `g.cpo_name(args...)` or `uv.cpo_name(args...)` 2. **ADL function**: unqualified `cpo_name(args...)` found via argument-dependent lookup 3. **Default**: library-provided fallback (if available) -The `edge_value` and `target_id` CPOs have an extended resolution chain that -also checks for data members (`.value`, `.target_id`) and tuple-like access -(`get`). +Some CPOs have extended resolution chains: + +- **`num_vertices(g)`** adds a tier between ADL and the graph-as-range fallback: + 3. `std::ranges::size(vertices(g))` — vertex range size (fires for any graph that provides `vertices(g)`) + 4. `std::ranges::size(g)` — graph is itself a `sized_range` +- **`edge_value`** and **`target_id`** also check for data members (`.value`, `.target_id`) and tuple-like access (`get`). --- diff --git a/docs/user-guide/adjacency-lists.md b/docs/user-guide/adjacency-lists.md index d986bc7..c946319 100644 --- a/docs/user-guide/adjacency-lists.md +++ b/docs/user-guide/adjacency-lists.md @@ -80,6 +80,8 @@ automatically; map-based containers (`map`, `unordered_map`) satisfy All graph operations are **Customization Point Objects (CPOs)** that resolve in three tiers: custom member → ADL free function → built-in default. +Some CPOs extend this chain; `num_vertices(g)` for example checks +`size(vertices(g))` before falling back to `size(g)`. ### Structure CPOs diff --git a/examples/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp b/examples/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp new file mode 100644 index 0000000..24bdfa6 --- /dev/null +++ b/examples/AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp @@ -0,0 +1,154 @@ +// Copyright (C) 2025 Andrzej Krzemienski. +// +// 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) +// +// This example demonstrates how to adapt an existing third-party graph container +// for use with the graph-v3 library using Customization Point Objects (CPOs). +// +// graph-v3 dispatches all graph operations (vertex iteration, edge access, ID +// extraction, …) through CPOs rather than through a traits specialisation or +// virtual functions. A type satisfies graph::adjacency_list once a small +// set of ADL-findable free functions has been provided in the type's own +// namespace. No changes to the third-party type itself are required. +// +// The four functions below are all that is needed: +// +// vertices(g) — range of vertices, wrapped into a vertex_descriptor_view +// edges(g, u) — out-edge range for a vertex_descriptor u +// target_id(g, uv) — target vertex ID from an edge_descriptor uv +// vertex_value(g, u) — user-visible vertex data (MyVertex) for a vertex_descriptor u +// +// The trailing-return-type SFINAE pattern keeps the templates narrow: they are +// excluded from overload resolution automatically when the argument does not +// have the expected interface (e.g. when u is a plain integer rather than a +// vertex_descriptor, or uv is not an edge_descriptor). + +#include // vertices, out_edges, vertex_value, vertexlist, + // vertices_dfs, index_adjacency_list, … +#include +#include +#include +#include + + +namespace MyLibrary { // ── Third-party graph container (left unchanged) ──────── + +struct MyEdge { + std::string content; + int indexOfTarget; +}; + +struct MyVertex { + std::string content; + std::vector outEdges; +}; + +class MyGraph { + std::vector _vertices; + +public: + MyVertex const* getVertexByIndex(int index) const { return &_vertices[static_cast(index)]; } + + std::vector const& getAllVertices() const { return _vertices; } + + void setTopology(std::vector t) { _vertices = std::move(t); } +}; + +} // namespace MyLibrary + + +namespace MyLibrary { // ── graph-v3 CPO customisation (unintrusive) ──────────── + +// --------------------------------------------------------------------------- +// vertices(g) +// Returns a range over all vertices. Because std::vector::const_iterator +// is a random-access iterator, the CPO wraps the result in a +// vertex_descriptor_view whose storage_type is size_t. Vertex IDs are +// therefore contiguous integral indices, and find_vertex / num_vertices +// work via their built-in random-access defaults — no extra code needed. +// --------------------------------------------------------------------------- +auto vertices(const MyGraph& g) { + return std::views::all(g.getAllVertices()); +} + +// --------------------------------------------------------------------------- +// edges(g, u) — u is a graph-v3 vertex_descriptor (stores a size_t index) +// The trailing return type makes the function SFINAE-friendly: it is +// excluded from overload resolution for argument types that lack vertex_id() +// (e.g. a plain size_t), so the CPO's built-in default path (which calls +// find_vertex then edges(g, descriptor)) remains available. +// The CPO wraps the returned range in an edge_descriptor_view automatically. +// --------------------------------------------------------------------------- +template +auto edges(const MyGraph& g, const VDesc& u) + -> decltype(std::views::all(g.getAllVertices()[u.vertex_id()].outEdges)) { + return std::views::all(g.getAllVertices()[u.vertex_id()].outEdges); +} + +// --------------------------------------------------------------------------- +// target_id(g, uv) — uv is a graph-v3 edge_descriptor +// uv.value() returns an iterator to the underlying MyEdge. Dereferencing +// it gives the MyEdge from which we read indexOfTarget. +// The trailing return type restricts the function to edge descriptors that +// expose a value() iterator (SFINAE). +// --------------------------------------------------------------------------- +template +auto target_id(const MyGraph&, const EdgeDesc& uv) + -> decltype((*uv.value()).indexOfTarget) { + return (*uv.value()).indexOfTarget; +} + +// --------------------------------------------------------------------------- +// vertex_value(g, u) — u is a graph-v3 vertex_descriptor +// Returns the MyVertex data for use in display or further inspection. +// The default CPO path (u.inner_value(g)) would call MyGraph::operator[], +// which MyGraph does not provide, so we supply this ADL override instead. +// --------------------------------------------------------------------------- +template +auto vertex_value(const MyGraph& g, const VDesc& u) + -> decltype(g.getAllVertices()[u.vertex_id()]) { + return g.getAllVertices()[u.vertex_id()]; +} + +} // namespace MyLibrary + + +int main() { + // Verify concept satisfaction at compile time. + // index_adjacency_list is required by vertices_dfs (integral vertex IDs, + // random-access vertex storage). + static_assert(graph::adjacency_list); + static_assert(graph::index_adjacency_list); + + const MyLibrary::MyGraph g = []{ // ── build the graph ─────────────────── + MyLibrary::MyGraph r; + std::vector topo{ + // A | + /*0*/ {"A", {{"", 1}, {"", 2}}}, // / \ | + /*1*/ {"B", {{"", 3}}}, // B C | + /*2*/ {"C", {{"", 3}}}, // \ / | + /*3*/ {"D", {}} // D | + }; + r.setTopology(std::move(topo)); + return r; + }(); + + // DFS traversal starting from vertex 0. + // + // vertices_dfs yields one vertex_descriptor per visited vertex via a + // structured binding [v]. graph::vertex_value(g, v) fetches the + // associated MyVertex so we can access its 'content' field. + // + // graph-v3 v2 used [vid, v] where v was a raw vertex reference. + // In v3, v is an opaque vertex_descriptor; content is accessed via the + // vertex_value CPO. + for (auto [v] : graph::views::vertices_dfs(g, std::size_t{0})) + std::cout << graph::vertex_value(g, v).content << " "; + + std::cout << "\n"; + + // Expected output: A B D C (or A C D B, depending on DFS tie-breaking) + return 0; +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6b12faa..329a0b5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,10 @@ add_executable(basic_usage basic_usage.cpp) target_link_libraries(basic_usage PRIVATE graph3) +add_executable(adapting_third_party_graph + AdaptingThirdPartyGraph/adapting_a_third_party_graph.cpp) +target_link_libraries(adapting_third_party_graph PRIVATE graph3) + add_executable(dijkstra_example dijkstra_clrs_example.cpp) target_link_libraries(dijkstra_example PRIVATE graph3) target_include_directories(dijkstra_example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/include/graph/adj_list/detail/graph_cpo.hpp b/include/graph/adj_list/detail/graph_cpo.hpp index df74baa..c8ed0af 100644 --- a/include/graph/adj_list/detail/graph_cpo.hpp +++ b/include/graph/adj_list/detail/graph_cpo.hpp @@ -1352,7 +1352,10 @@ namespace _cpo_impls { // ========================================================================= namespace _num_vertices { - enum class _St { _none, _member, _adl, _ranges }; + // Use the public vertices CPO (already declared above) + using _cpo_instances::vertices; + + enum class _St { _none, _member, _adl, _vertices_size, _ranges }; // Check for g.num_vertices() member function template @@ -1366,6 +1369,12 @@ namespace _cpo_impls { { num_vertices(g) } -> std::integral; }; + // Check if vertices(g) returns a sized_range (uses the vertices CPO) + template + concept _has_vertices_size = requires(const G& g) { + { vertices(g) } -> std::ranges::sized_range; + }; + // Check if graph is a sized_range (default) template concept _has_ranges = std::ranges::sized_range; @@ -1376,6 +1385,8 @@ namespace _cpo_impls { return {_St::_member, noexcept(std::declval().num_vertices())}; } else if constexpr (_has_adl) { return {_St::_adl, noexcept(num_vertices(std::declval()))}; + } else if constexpr (_has_vertices_size) { + return {_St::_vertices_size, noexcept(std::ranges::size(vertices(std::declval())))}; } else if constexpr (_has_ranges) { return {_St::_ranges, noexcept(std::ranges::size(std::declval()))}; } else { @@ -1392,12 +1403,14 @@ namespace _cpo_impls { /** * @brief Get the number of vertices in the graph * - * Resolution order (three-tier approach): + * Resolution order (four-tier approach): * 1. g.num_vertices() - Member function (highest priority) - * 2. num_vertices(g) - ADL (medium priority) - * 3. std::ranges::size(g) - Ranges default (lowest priority) + * 2. num_vertices(g) - ADL (high priority) + * 3. std::ranges::size(vertices(g)) - Vertices range size (medium priority) + * 4. std::ranges::size(g) - Ranges default (lowest priority) * - * The default implementation works for any sized_range (vector, deque, map, etc.) + * The default implementations work for any graph whose vertices(g) returns + * a sized_range, or any graph that is itself a sized_range. * Custom graph types can override by providing a member function or ADL function. * * @tparam G Graph type @@ -1414,6 +1427,8 @@ namespace _cpo_impls { return g.num_vertices(); } else if constexpr (_Choice<_G>._Strategy == _St::_adl) { return num_vertices(g); + } else if constexpr (_Choice<_G>._Strategy == _St::_vertices_size) { + return std::ranges::size(vertices(g)); } else if constexpr (_Choice<_G>._Strategy == _St::_ranges) { return std::ranges::size(g); } @@ -1468,12 +1483,14 @@ namespace _cpo_impls { /** * @brief Get the number of vertices in the graph * - * Resolution order (three-tier approach): + * Resolution order (four-tier approach): * 1. g.num_vertices() - Member function (highest priority) - * 2. num_vertices(g) - ADL (medium priority) - * 3. std::ranges::size(g) - Ranges default (lowest priority) + * 2. num_vertices(g) - ADL (high priority) + * 3. std::ranges::size(vertices(g)) - Vertices range size (medium priority) + * 4. std::ranges::size(g) - Ranges default (lowest priority) * - * The default implementation works for any sized_range (vector, deque, map, etc.) + * The default implementations work for any graph whose vertices(g) returns + * a sized_range, or any graph that is itself a sized_range. * Custom graph types can override by providing a member function or ADL function. * * @tparam G Graph type @@ -1490,6 +1507,8 @@ namespace _cpo_impls { return g.num_vertices(); } else if constexpr (_Choice<_G>._Strategy == _St::_adl) { return num_vertices(g); + } else if constexpr (_Choice<_G>._Strategy == _St::_vertices_size) { + return std::ranges::size(vertices(g)); } else if constexpr (_Choice<_G>._Strategy == _St::_ranges) { return std::ranges::size(g); } From 5ae6911902e72e38794e34e5e2f8feda7d91ac09 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 30 Apr 2026 12:17:57 -0400 Subject: [PATCH 03/20] examples: add CppCon2021 examples refactored from graph-v2 Copies the CppCon 2021 conference examples into examples/CppCon2021/ and refactors them from the v2 API (vertex/edge references) to the v3 API (descriptors, CPOs). Key v2 -> v3 changes: - utilities.hpp: remove graph::basic_adjacency_list constraint (not in v3); fix join() to read plain integer edge values directly instead of calling graph::target_id CPO - graphs.cpp: breadth_first_search.hpp -> bfs.hpp; edges_breadth_first_search -> edges_bfs with std::size_t seed - bacon.cpp: sourced_edges_breadth_first_search([u,v,uv]) -> edges_bfs([uv]) + source_id/target_id CPOs inside loop - ospf.cpp: init_shortest_paths now takes graph as first arg; dijkstra uses container_value_fn for distance/predecessor; weight lambda signature changed to (const G&, const edge_t&) - imdb.cpp: same BFS and weight-function changes as bacon/ospf; kprop becomes a two-argument (g, uv) lambda CMakeLists: examples/CppCon2021/CMakeLists.txt owns the four targets; examples/CMakeLists.txt delegates via add_subdirectory(CppCon2021). --- examples/CMakeLists.txt | 2 + examples/CppCon2021/CMakeLists.txt | 21 ++ examples/CppCon2021/examples/bacon.cpp | 30 ++ examples/CppCon2021/examples/graphs.cpp | 219 +++++++++++++ examples/CppCon2021/examples/imdb.cpp | 86 ++++++ examples/CppCon2021/examples/ospf.cpp | 109 +++++++ examples/CppCon2021/graphs/imdb-graph.hpp | 262 ++++++++++++++++ examples/CppCon2021/graphs/karate-graph.hpp | 163 ++++++++++ examples/CppCon2021/graphs/ospf-graph.hpp | 205 +++++++++++++ examples/CppCon2021/graphs/spice-graph.hpp | 129 ++++++++ examples/CppCon2021/include/utilities.hpp | 322 ++++++++++++++++++++ 11 files changed, 1548 insertions(+) create mode 100644 examples/CppCon2021/CMakeLists.txt create mode 100644 examples/CppCon2021/examples/bacon.cpp create mode 100644 examples/CppCon2021/examples/graphs.cpp create mode 100644 examples/CppCon2021/examples/imdb.cpp create mode 100644 examples/CppCon2021/examples/ospf.cpp create mode 100644 examples/CppCon2021/graphs/imdb-graph.hpp create mode 100644 examples/CppCon2021/graphs/karate-graph.hpp create mode 100644 examples/CppCon2021/graphs/ospf-graph.hpp create mode 100644 examples/CppCon2021/graphs/spice-graph.hpp create mode 100644 examples/CppCon2021/include/utilities.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 329a0b5..a2f69b4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable(dijkstra_example dijkstra_clrs_example.cpp) target_link_libraries(dijkstra_example PRIVATE graph3) target_include_directories(dijkstra_example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +add_subdirectory(CppCon2021) + # BGL adaptor example (requires Boost headers) option(BUILD_BGL_EXAMPLES "Build BGL adaptor examples (requires Boost headers)" OFF) diff --git a/examples/CppCon2021/CMakeLists.txt b/examples/CppCon2021/CMakeLists.txt new file mode 100644 index 0000000..85de257 --- /dev/null +++ b/examples/CppCon2021/CMakeLists.txt @@ -0,0 +1,21 @@ +# CppCon 2021 examples — refactored from graph-v2 to graph-v3 + +set(CPPCON21_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/graphs) + +add_executable(cppcon21_graphs examples/graphs.cpp) +target_link_libraries(cppcon21_graphs PRIVATE graph3) +target_include_directories(cppcon21_graphs PRIVATE ${CPPCON21_INCLUDE_DIRS}) + +add_executable(cppcon21_bacon examples/bacon.cpp) +target_link_libraries(cppcon21_bacon PRIVATE graph3) +target_include_directories(cppcon21_bacon PRIVATE ${CPPCON21_INCLUDE_DIRS}) + +add_executable(cppcon21_ospf examples/ospf.cpp) +target_link_libraries(cppcon21_ospf PRIVATE graph3) +target_include_directories(cppcon21_ospf PRIVATE ${CPPCON21_INCLUDE_DIRS}) + +add_executable(cppcon21_imdb examples/imdb.cpp) +target_link_libraries(cppcon21_imdb PRIVATE graph3) +target_include_directories(cppcon21_imdb PRIVATE ${CPPCON21_INCLUDE_DIRS}) diff --git a/examples/CppCon2021/examples/bacon.cpp b/examples/CppCon2021/examples/bacon.cpp new file mode 100644 index 0000000..40bffd8 --- /dev/null +++ b/examples/CppCon2021/examples/bacon.cpp @@ -0,0 +1,30 @@ +#include "graph/graph.hpp" +#include "graph/views/bfs.hpp" +#include "imdb-graph.hpp" + +#include +#include +#include + +std::vector> costars{{1, 5, 6}, {7, 10, 0, 5, 12}, {4, 3, 11}, {2, 11}, {8, 9, 2, 12}, {0, 1}, + {7, 0}, {6, 1, 10}, {4, 9}, {4, 8}, {7, 1}, {2, 3}, + {1, 4}}; + +int main() { + + std::vector bacon_number(size(actors)); + + // In v3, edges_bfs yields [uv] (edge descriptor only). + // Extract source and target IDs from the edge descriptor via CPOs. + for (auto&& [uv] : graph::views::edges_bfs(costars, std::size_t{1})) { + auto u = graph::source_id(costars, uv); + auto v = graph::target_id(costars, uv); + bacon_number[v] = bacon_number[u] + 1; + } + + for (size_t i = 0; i < size(actors); ++i) { + std::cout << actors[i] << " has Bacon number " << bacon_number[i] << std::endl; + } + + return 0; +} diff --git a/examples/CppCon2021/examples/graphs.cpp b/examples/CppCon2021/examples/graphs.cpp new file mode 100644 index 0000000..c06bcd8 --- /dev/null +++ b/examples/CppCon2021/examples/graphs.cpp @@ -0,0 +1,219 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// +// Make sure various combinations of graphs can compile + +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wunused-result" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4834) // warning C4834: discarding return value of function with 'nodiscard' attribute +# pragma warning(disable : 4267) // warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data +#endif + +#include +#include +#include +#include + +#include "graph/graph.hpp" +#include "graph/views/bfs.hpp" + +#include "imdb-graph.hpp" +#include "karate-graph.hpp" +#include "ospf-graph.hpp" +#include "spice-graph.hpp" +#include "utilities.hpp" + +int main() { + + /** + * Karate is only represented as index edge list and index adjacency list + */ + std::vector> G(34); + push_back_plain_fill(karate_index_edge_list, G, false, 0); + static_assert(graph::adjacency_list); + std::cout << "Karate adjacency list:\n"; + std::cout << "size = " << G.size() << std::endl; + for (size_t uid = 0; uid < G.size(); ++uid) { + std::cout << std::setw(3) << uid << ":"; + for (auto&& vid : G[uid]) { + std::cout << " " << vid; + } + std::cout << std::endl; + } + + std::vector>> H(34); + push_back_plain_fill(karate_index_edge_list, H, false, 0); + std::cout << "\nKarate (edge_list plain fill):\n"; + std::cout << "size = " << H.size() << std::endl; + for (size_t uid = 0; uid < H.size(); ++uid) { + std::cout << std::setw(3) << uid << ":"; + for (auto&& [vid] : H[uid]) { + std::cout << " " << vid; + } + std::cout << std::endl; + } + + + push_back_fill(karate_index_edge_list, H, false, 0); + std::cout << "\nKarate (edge_list fill...adding more):\n"; + std::cout << "size = " << H.size() << std::endl; + for (size_t uid = 0; uid < H.size(); ++uid) { + std::cout << std::setw(3) << uid << ":"; + for (auto&& [vid] : H[uid]) { + std::cout << " " << vid; + } + std::cout << std::endl; + } + + //---------------------------------------------------------------------------- + + /** + * Other graphs have vertices and edges tables + */ + auto a = make_plain_graph(ospf_vertices, ospf_edges); + std::cout << "\nOSPF plain graph:\n"; + std::cout << "size = " << a.size() << std::endl; + for (size_t uid = 0; uid < a.size(); ++uid) { + std::cout << std::setw(3) << ospf_vertices[uid] << ":"; + for (auto&& vid : a[uid]) { + std::cout << " " << ospf_vertices[vid]; + } + std::cout << std::endl; + } + + auto b = make_property_graph(ospf_vertices, ospf_edges); + std::cout << "\nOSPF property graph:\n"; + std::cout << "size = " << b.size() << std::endl; + for (size_t uid = 0; uid < b.size(); ++uid) { + std::cout << std::setw(3) << ospf_vertices[uid] << ":"; + for (auto&& [vid, val] : b[uid]) { + std::cout << " " << ospf_vertices[vid] << ":" << val; + } + std::cout << std::endl; + } + + auto c = make_index_graph(ospf_vertices, ospf_edges); + std::cout << "\nOSPF index graph:\n"; + std::cout << "size = " << c.size() << std::endl; + for (size_t uid = 0; uid < c.size(); ++uid) { + std::cout << std::setw(3) << ospf_vertices[uid] << ":"; + for (auto&& [vid, val] : c[uid]) { + std::cout << " " << ospf_vertices[vid] << ":" << std::get<2>(ospf_edges[val]); + } + std::cout << std::endl; + } + + auto d = make_plain_graph>>( + ospf_vertices, ospf_edges, true); + std::cout << "\nOSPF plain graph (vector of lists):\n"; + std::cout << "size = " << d.size() << std::endl; + for (size_t uid = 0; uid < d.size(); ++uid) { + std::cout << std::setw(3) << ospf_vertices[uid] << ":"; + for (auto&& vid : d[uid]) { + std::cout << " " << ospf_vertices[vid]; + } + std::cout << std::endl; + } + + auto e = make_index_graph>>>(ospf_vertices, ospf_edges, true); + std::cout << "\nOSPF index graph (vector of vector of tuples):\n"; + std::cout << "size = " << e.size() << std::endl; + for (size_t uid = 0; uid < e.size(); ++uid) { + std::cout << std::setw(3) << ospf_vertices[uid] << ":"; + for (auto&& [vid, val] : e[uid]) { + std::cout << " " << ospf_vertices[vid] << ":" << std::get<2>(ospf_edges[val]); + } + std::cout << std::endl; + } + + //---------------------------------------------------------------------------- + + auto [f, g] = make_plain_bipartite_graphs<>(movies, actors, movies_actors); + auto h = make_plain_bipartite_graph(movies, actors, movies_actors, 0); + auto i = make_plain_bipartite_graph(movies, actors, movies_actors, 1); + std::cout << "\nMovies-actors plain bipartite graphs\n"; + std::cout << "index 0: " << f.size() << "==" << h.size() << std::endl; + for (size_t uid = 0; uid < f.size(); ++uid) { + std::cout << std::setw(20) << movies[uid] << ": |"; + for (auto&& vid : f[uid]) { + std::cout << actors[vid] << "|"; + } + std::cout << std::endl; + } + std::cout << "index 1: " << g.size() << "==" << i.size() << std::endl; + for (size_t uid = 0; uid < g.size(); ++uid) { + std::cout << std::setw(20) << actors[uid] << ": |"; + for (auto&& vid : g[uid]) { + std::cout << movies[vid] << "|"; + } + std::cout << std::endl; + } + + auto [j, k] = make_plain_bipartite_graphs>>(movies, actors, movies_actors); + auto l = make_plain_bipartite_graph>>(movies, actors, movies_actors, 0); + auto m = make_plain_bipartite_graph>>(movies, actors, movies_actors, 1); + std::cout << "\nMovies-actors plain bipartite graphs (vector of lists)\n"; + std::cout << "index 0: " << j.size() << "==" << l.size() << std::endl; + for (size_t uid = 0; uid < j.size(); ++uid) { + std::cout << std::setw(20) << movies[uid] << ": |"; + for (auto&& vid : j[uid]) { + std::cout << actors[vid] << "|"; + } + std::cout << std::endl; + } + std::cout << "index 1: " << k.size() << "==" << m.size() << std::endl; + for (size_t uid = 0; uid < k.size(); ++uid) { + std::cout << std::setw(20) << actors[uid] << ": |"; + for (auto&& vid : k[uid]) { + std::cout << movies[vid] << "|"; + } + std::cout << std::endl; + } + + //---------------------------------------------------------------------------- + + auto n = make_plain_graph>>( + spice_vertices, spice_edges); + auto o = make_plain_graph>>( + spice_vertices, spice_edges_values); + auto p = make_index_graph(spice_vertices, spice_edges); + auto q = make_index_graph(spice_vertices, spice_edges_values); + auto r = make_property_graph(spice_vertices, spice_edges); + auto s = make_property_graph(spice_vertices, spice_edges_values); + + std::cout << "\nSpice property graph (using edges+values)\n"; + std::cout << "Size: " << s.size() << std::endl; + for (size_t uid = 0; uid < s.size(); ++uid) { + std::cout << std::setw(4) << spice_vertices[uid] << ": |"; + for (auto&& [vid, comp, val] : s[uid]) { + std::cout << std::setw(3) << spice_vertices[vid] << ":" << comp << "/" << val << "|"; + } + std::cout << std::endl; + } + + // Compile-check: create BFS edge views (results discarded) + graph::views::edges_bfs(n, std::size_t{1}); + graph::views::edges_bfs(o, std::size_t{1}); + graph::views::edges_bfs(p, std::size_t{1}); + graph::views::edges_bfs(q, std::size_t{0}); + + return 0; +} + +#if defined(__clang__) || defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/examples/CppCon2021/examples/imdb.cpp b/examples/CppCon2021/examples/imdb.cpp new file mode 100644 index 0000000..7729c13 --- /dev/null +++ b/examples/CppCon2021/examples/imdb.cpp @@ -0,0 +1,86 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// + +// IMDB Example + +#if defined(__GNUC__) || defined(__clang__) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4267) // warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data +#endif + +#include +#include +#include + +#include "graph/graph.hpp" +#include "graph/views/bfs.hpp" +#include "imdb-graph.hpp" +#include "utilities.hpp" + +int main() { + + // Get actor-movie and movie-actor graphs (plain: vector>) + auto&& [G, H] = make_plain_bipartite_graphs<>(movies, actors, movies_actors); + + // Will also work with this type of graph (vector>>) + auto&& [J, K] = make_bipartite_graphs<>(movies, actors, movies_actors); + + // Create actor-actor graph: L[actor] -> list of {co-star, movie} + auto L = join(G, H); + auto M = join(H, G); + + // Can also join with explicit IndexGraph types + auto N = join>>>(G, H); + auto O = join>>>(H, G); + + size_t kevin_bacon = 1; + std::vector distance(L.size()); + std::vector parents(L.size()); + std::vector together_in(L.size()); + + // In v3, edges_bfs yields [uv] without source. + // Pass a value function to also get the movie index per edge. + // The edge tuple in L is {target_actor_id, movie_id}; *uv.value() gives + // the raw tuple, so std::get<1>(*uv.value()) is the movie index. + auto kprop = [](const auto& /*g*/, const auto& uv) { + return std::get<1>(*uv.value()); // movie index stored as second element + }; + + for (auto&& [uv, k] : graph::views::edges_bfs(L, kevin_bacon, kprop)) { + auto u = graph::source_id(L, uv); + auto v = graph::target_id(L, uv); + distance[v] = distance[u] + 1; + parents[v] = u; + together_in[v] = k; + } + + std::cout << actors[kevin_bacon] << " has a bacon number of " << distance[kevin_bacon] << std::endl; + std::cout << std::endl; + + // Iterate through all actors (other than Kevin Bacon) + for (size_t i = 0; i < actors.size(); ++i) { + if (i != kevin_bacon) { + auto bacon_number = distance[i]; + std::cout << actors[i] << " has a bacon number of " << distance[i] << std::endl; + + auto k = i; + size_t d = distance[k]; + while (k != kevin_bacon) { + std::cout << " " << actors[k] << " starred with " << actors[parents[k]] << " in " << movies[together_in[k]] + << std::endl; + k = parents[k]; + if (d-- == 0) { + break; + } + } + std::cout << std::endl; + } + } + + return 0; +} diff --git a/examples/CppCon2021/examples/ospf.cpp b/examples/CppCon2021/examples/ospf.cpp new file mode 100644 index 0000000..ee3e619 --- /dev/null +++ b/examples/CppCon2021/examples/ospf.cpp @@ -0,0 +1,109 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// + +#include +#include +#include + +#include "graph/algorithm/dijkstra_shortest_paths.hpp" +#include "ospf-graph.hpp" +#include "utilities.hpp" + +int main() { + + static_assert(graph::adjacency_list); + + // ── ospf_index_adjacency_list: vector>> ────── + // Each edge tuple is {target_id, weight}. + // In v3: init_shortest_paths takes the graph first; dijkstra uses + // container_value_fn to adapt vectors into (g, uid) -> value& functions, + // and the weight function signature is (const G&, const edge_t&). + + std::vector d(size(ospf_index_adjacency_list)); + std::vector p(size(ospf_index_adjacency_list)); + graph::init_shortest_paths(ospf_index_adjacency_list, d, p); + graph::dijkstra_shortest_paths( + ospf_index_adjacency_list, + std::size_t{5}, + graph::container_value_fn(d), + graph::container_value_fn(p), + [](const auto& /*g*/, const auto& uv) { return std::get<1>(*uv.value()); }); + + std::cout << "----------------" << std::endl; + std::cout << "Contents of ospf_index_adjacency_list (the correct answer)" << std::endl; + + for (size_t i = 0; i < size(ospf_vertices); ++i) { + std::cout << std::setw(6) << ospf_vertices[i] << std::setw(6) << d[i] << std::endl; + } + + std::cout << "----------------" << std::endl; + std::cout << "Results from make_property_graph(osp_vertices)" << std::endl; + + // ── make_property_graph: vector>> ──────────── + // Each edge tuple is {target_id, weight} (property = weight). + + auto G = make_property_graph(ospf_vertices, ospf_edges, true); + + // Alternatively + auto H = make_property_graph>>>(ospf_vertices, ospf_edges, true); + auto I = make_property_graph>>>(ospf_vertices, ospf_edges, true); + + static_assert(graph::adjacency_list); + + p.resize(graph::num_vertices(G)); + std::vector e(graph::num_vertices(G)); + graph::init_shortest_paths(G, e, p); + graph::dijkstra_shortest_paths( + G, + std::size_t{5}, + graph::container_value_fn(e), + graph::container_value_fn(p), + [](const auto& /*g*/, const auto& uv) { return std::get<1>(*uv.value()); }); + + bool pass = true; + for (size_t i = 0; i < size(ospf_vertices); ++i) { + std::cout << std::setw(6) << ospf_vertices[i] << std::setw(6) << e[i] << std::endl; + if (e[i] != d[i]) + pass = false; + } + std::cout << (pass ? "***PASS***" : "***FAIL***") << std::endl; + + std::cout << "----------------" << std::endl; + std::cout << "Results from make_index_graph(osp_vertices)" << std::endl; + + // ── make_index_graph: vector>> ─────────────── + // Each edge tuple is {target_id, edge_index_into_ospf_edges}. + // Access the original weight via ospf_edges[edge_index]. + + auto J = make_index_graph(ospf_vertices, ospf_edges, true); + + p.resize(graph::num_vertices(J)); + std::vector f(graph::num_vertices(J)); + graph::init_shortest_paths(J, f, p); + graph::dijkstra_shortest_paths( + J, + std::size_t{5}, + graph::container_value_fn(f), + graph::container_value_fn(p), + [](const auto& /*g*/, const auto& uv) { + // *uv.value() = tuple + auto edge_index = std::get<1>(*uv.value()); + return std::get<2>(ospf_edges[edge_index]); + }); + + bool pass2 = true; + for (size_t i = 0; i < size(ospf_vertices); ++i) { + std::cout << std::setw(6) << ospf_vertices[i] << std::setw(6) << e[i] << std::endl; + if (e[i] != d[i]) + pass2 = false; + } + std::cout << (pass2 ? "***PASS***" : "***FAIL***") << std::endl; + + return 0; +} diff --git a/examples/CppCon2021/graphs/imdb-graph.hpp b/examples/CppCon2021/graphs/imdb-graph.hpp new file mode 100644 index 0000000..89dbf34 --- /dev/null +++ b/examples/CppCon2021/graphs/imdb-graph.hpp @@ -0,0 +1,262 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// + +// IMDB Example + + +#include +#include +#include + + +// clang-format off + +std::vector actors{ + "Tom Cruise", + "Kevin Bacon", + "Hugo Weaving", + "Carrie-Anne Moss", + "Natalie Portman", + "Jack Nicholson", + "Kelly McGillis", + "Harrison Ford", + "Sebastian Stan", + "Mila Kunis", + "Michelle Pfeiffer", + "Keanu Reeves", + "Julia Roberts", +}; + + +std::vector movies{ + "A Few Good Men", + "Top Gun", + "Black Swan", + "V for Vendetta", + "The Matrix", + "Witness", + "What Lies Beneath", + "Closer", + "Flatliners", +}; + + +std::vector> movies_actors{ + {"A Few Good Men", "Tom Cruise"}, + {"A Few Good Men", "Kevin Bacon"}, + {"A Few Good Men", "Jack Nicholson"}, + {"What Lies Beneath", "Harrison Ford"}, + {"What Lies Beneath", "Kevin Bacon"}, + {"Top Gun", "Tom Cruise"}, + {"Top Gun", "Kelly McGillis"}, + {"Witness", "Harrison Ford"}, + {"Witness", "Kelly McGillis"}, + {"Black Swan", "Sebastian Stan"}, + {"Black Swan", "Natalie Portman"}, + {"Black Swan", "Mila Kunis"}, + {"V for Vendetta", "Hugo Weaving"}, + {"V for Vendetta", "Natalie Portman"}, + {"The Matrix", "Carrie-Anne Moss"}, + {"The Matrix", "Keanu Reeves"}, + {"The Matrix", "Hugo Weaving"}, + {"What Lies Beneath", "Michelle Pfeiffer"}, + {"Closer", "Natalie Portman"}, + {"Closer", "Julia Roberts"}, + {"Flatliners", "Kevin Bacon"}, + {"Flatliners", "Julia Roberts"}, +}; + + +std::vector> movie_actor_edge_list { +{ 0, 0}, +{ 0, 1}, +{ 0, 5}, +{ 1, 0}, +{ 1, 6}, +{ 2, 8}, +{ 2, 4}, +{ 2, 9}, +{ 3, 2}, +{ 3, 4}, +{ 4, 3}, +{ 4, 11}, +{ 4, 2}, +{ 5, 7}, +{ 5, 6}, +{ 6, 7}, +{ 6, 1}, +{ 6, 10}, +{ 7, 4}, +{ 7, 12}, +{ 8, 1}, +{ 8, 12}, +}; + + + std::vector> actor_movie_edge_list { +{ 0, 0}, +{ 0, 1}, +{ 1, 0}, +{ 1, 6}, +{ 1, 8}, +{ 2, 3}, +{ 2, 4}, +{ 3, 4}, +{ 4, 2}, +{ 4, 3}, +{ 4, 7}, +{ 5, 0}, +{ 6, 1}, +{ 6, 5}, +{ 7, 6}, +{ 7, 5}, +{ 8, 2}, +{ 9, 2}, +{ 10, 6}, +{ 11, 4}, +{ 12, 7}, +{ 12, 8}, + }; + + +std::vector> movie_actor_index_adjacency_list { + /* 0*/ { 0, 1, 5, }, + /* 1*/ { 0, 6, }, + /* 2*/ { 8, 4, 9, }, + /* 3*/ { 2, 4, }, + /* 4*/ { 3, 11, 2, }, + /* 5*/ { 7, 6, }, + /* 6*/ { 7, 1, 10, }, + /* 7*/ { 4, 12, }, + /* 8*/ { 1, 12, }, +}; + + +std::vector> actor_movie_index_adjacency_list { + /* 0*/ { 0, 1, }, + /* 1*/ { 0, 6, 8,}, + /* 2*/ { 3, 4, }, + /* 3*/ { 4, }, + /* 4*/ { 2, 3, 7,}, + /* 5*/ { 0, }, + /* 6*/ { 1, 5, }, + /* 7*/ { 6, 5, }, + /* 8*/ { 2, }, + /* 9*/ { 2, }, + /* 10*/ { 6, }, + /* 11*/ { 4, }, + /* 12*/ { 7, 8, }, +}; + + +std::vector> actor_actor_edge_list { +{ 0, 1, 0}, +{ 0, 5, 0}, +{ 0, 6, 1}, +{ 1, 0, 0}, +{ 1, 5, 0}, +{ 1, 7, 6}, +{ 1, 10, 6}, +{ 1, 12, 8}, +{ 2, 4, 3}, +{ 2, 3, 4}, +{ 2, 11, 4}, +{ 3, 11, 4}, +{ 3, 2, 4}, +{ 4, 8, 2}, +{ 4, 9, 2}, +{ 4, 2, 3}, +{ 4, 12, 7}, +{ 5, 0, 0}, +{ 5, 1, 0}, +{ 6, 0, 1}, +{ 6, 7, 5}, +{ 7, 1, 6}, +{ 7, 10, 6}, +{ 7, 6, 5}, +{ 8, 4, 2}, +{ 8, 9, 2}, +{ 9, 8, 2}, +{ 9, 4, 2}, +{ 10, 7, 6}, +{ 10, 1, 6}, +{ 11, 3, 4}, +{ 11, 2, 4}, +{ 12, 4, 7}, +{ 12, 1, 8}, +}; + +std::vector>> actor_actor_adjacency_list { + /* 0*/ { { 1, 0 }, { 5, 0 }, { 6, 1 }, }, + /* 1*/ { { 0, 0 }, { 5, 0 }, { 7, 6 }, { 10, 6 }, { 12, 8 },}, + /* 2*/ { { 4, 3 }, { 3, 4 }, { 11, 4 }, }, + /* 3*/ { { 11, 4 }, { 2, 4 }, }, + /* 4*/ { { 8, 2 }, { 9, 2 }, { 2, 3 }, { 12, 7 }, }, + /* 5*/ { { 0, 0 }, { 1, 0 }, }, + /* 6*/ { { 0, 1 }, { 7, 5 }, }, + /* 7*/ { { 1, 6 }, { 10, 6 }, { 6, 5 }, }, + /* 8*/ { { 4, 2 }, { 9, 2 }, }, + /* 9*/ { { 8, 2 }, { 4, 2 }, }, + /* 10*/ { { 7, 6 }, { 1, 6 }, }, + /* 11*/ { { 3, 4 }, { 2, 4 }, }, + /* 12*/ { { 4, 7 }, { 1, 8 }, }, +}; + +std::vector> movie_movie_edge_list { +{ 0, 1, 0}, +{ 0, 5, 0}, +{ 0, 6, 1}, +{ 1, 0, 0}, +{ 1, 5, 0}, +{ 1, 7, 6}, +{ 1, 10, 6}, +{ 1, 12, 8}, +{ 2, 4, 3}, +{ 2, 3, 4}, +{ 2, 11, 4}, +{ 3, 11, 4}, +{ 3, 2, 4}, +{ 4, 8, 2}, +{ 4, 9, 2}, +{ 4, 2, 3}, +{ 4, 12, 7}, +{ 5, 0, 0}, +{ 5, 1, 0}, +{ 6, 0, 1}, +{ 6, 7, 5}, +{ 7, 1, 6}, +{ 7, 10, 6}, +{ 7, 6, 5}, +{ 8, 4, 2}, +{ 8, 9, 2}, +{ 9, 8, 2}, +{ 9, 4, 2}, +{ 10, 7, 6}, +{ 10, 1, 6}, +{ 11, 3, 4}, +{ 11, 2, 4}, +{ 12, 4, 7}, +{ 12, 1, 8}, +}; + +std::vector>> movie_movie_adjacency_list { + /* 0*/ { { 1, 0 }, { 5, 0 }, { 6, 1 }, }, + /* 1*/ { { 0, 0 }, { 5, 0 }, { 7, 6 }, { 10, 6 }, { 12, 8 },}, + /* 2*/ { { 4, 3 }, { 3, 4 }, { 11, 4 }, }, + /* 3*/ { { 11, 4 }, { 2, 4 }, }, + /* 4*/ { { 8, 2 }, { 9, 2 }, { 2, 3 }, { 12, 7 }, }, + /* 5*/ { { 0, 0 }, { 1, 0 }, }, + /* 6*/ { { 0, 1 }, { 7, 5 }, }, + /* 7*/ { { 1, 6 }, { 10, 6 }, { 6, 5 }, }, + /* 8*/ { { 4, 2 }, { 9, 2 }, }, + /* 9*/ { { 8, 2 }, { 4, 2 }, }, + /* 10*/ { { 7, 6 }, { 1, 6 }, }, + /* 11*/ { { 3, 4 }, { 2, 4 }, }, + /* 12*/ { { 4, 7 }, { 1, 8 }, }, +}; + +// clang-format on diff --git a/examples/CppCon2021/graphs/karate-graph.hpp b/examples/CppCon2021/graphs/karate-graph.hpp new file mode 100644 index 0000000..1396305 --- /dev/null +++ b/examples/CppCon2021/graphs/karate-graph.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#include + +// clang-format off + +std::vector> karate_index_edge_list { +{ 1, 0 }, +{ 2, 0 }, +{ 3, 0 }, +{ 4, 0 }, +{ 5, 0 }, +{ 6, 0 }, +{ 7, 0 }, +{ 8, 0 }, +{ 10, 0 }, +{ 11, 0 }, +{ 12, 0 }, +{ 13, 0 }, +{ 17, 0 }, +{ 19, 0 }, +{ 21, 0 }, +{ 31, 0 }, +{ 2, 1 }, +{ 3, 1 }, +{ 7, 1 }, +{ 13, 1 }, +{ 17, 1 }, +{ 19, 1 }, +{ 21, 1 }, +{ 30, 1 }, +{ 3, 2 }, +{ 7, 2 }, +{ 8, 2 }, +{ 9, 2 }, +{ 13, 2 }, +{ 27, 2 }, +{ 28, 2 }, +{ 32, 2 }, +{ 7, 3 }, +{ 12, 3 }, +{ 13, 3 }, +{ 6, 4 }, +{ 10, 4 }, +{ 6, 5 }, +{ 10, 5 }, +{ 16, 5 }, +{ 16, 6 }, +{ 30, 8 }, +{ 32, 8 }, +{ 33, 8 }, +{ 33, 9 }, +{ 33, 13 }, +{ 32, 14 }, +{ 33, 14 }, +{ 32, 15 }, +{ 33, 15 }, +{ 32, 18 }, +{ 33, 18 }, +{ 33, 19 }, +{ 32, 20 }, +{ 33, 20 }, +{ 32, 22 }, +{ 33, 22 }, +{ 25, 23 }, +{ 27, 23 }, +{ 29, 23 }, +{ 32, 23 }, +{ 33, 23 }, +{ 25, 24 }, +{ 27, 24 }, +{ 31, 24 }, +{ 31, 25 }, +{ 29, 26 }, +{ 33, 26 }, +{ 33, 27 }, +{ 31, 28 }, +{ 33, 28 }, +{ 32, 29 }, +{ 33, 29 }, +{ 32, 30 }, +{ 33, 30 }, +{ 32, 31 }, +{ 33, 31 }, +{ 33, 32 }, +}; + +std::vector> karate_directed_adjacency_list { + /* 0*/ { }, + /* 1*/ { 0,}, + /* 2*/ { 0, 1,}, + /* 3*/ { 0, 1, 2,}, + /* 4*/ { 0,}, + /* 5*/ { 0,}, + /* 6*/ { 0, 4, 5,}, + /* 7*/ { 0, 1, 2, 3,}, + /* 8*/ { 0, 2,}, + /* 9*/ { 2,}, + /* 10*/ { 0, 4, 5,}, + /* 11*/ { 0,}, + /* 12*/ { 0, 3,}, + /* 13*/ { 0, 1, 2, 3,}, + /* 14*/ { }, + /* 15*/ { }, + /* 16*/ { 5, 6,}, + /* 17*/ { 0, 1,}, + /* 18*/ { }, + /* 19*/ { 0, 1,}, + /* 20*/ { }, + /* 21*/ { 0, 1,}, + /* 22*/ { }, + /* 23*/ { }, + /* 24*/ { }, + /* 25*/ { 23, 24,}, + /* 26*/ { }, + /* 27*/ { 2, 23, 24,}, + /* 28*/ { 2,}, + /* 29*/ { 23, 26,}, + /* 30*/ { 1, 8,}, + /* 31*/ { 0, 24, 25, 28,}, + /* 32*/ { 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31,}, + /* 33*/ { 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32,}, +}; + +std::vector> karate_undirected_adjacency_list { + /* 0*/ { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 17, 19, 21, 31,}, + /* 1*/ { 0, 2, 3, 7, 13, 17, 19, 21, 30,}, + /* 2*/ { 0, 1, 3, 7, 8, 9, 13, 27, 28, 32,}, + /* 3*/ { 0, 1, 2, 7, 12, 13,}, + /* 4*/ { 0, 6, 10,}, + /* 5*/ { 0, 6, 10, 16,}, + /* 6*/ { 0, 4, 5, 16,}, + /* 7*/ { 0, 1, 2, 3,}, + /* 8*/ { 0, 2, 30, 32, 33,}, + /* 9*/ { 2, 33,}, + /* 10*/ { 0, 4, 5,}, + /* 11*/ { 0,}, + /* 12*/ { 0, 3,}, + /* 13*/ { 0, 1, 2, 3, 33,}, + /* 14*/ { 32, 33,}, + /* 15*/ { 32, 33,}, + /* 16*/ { 5, 6,}, + /* 17*/ { 0, 1,}, + /* 18*/ { 32, 33,}, + /* 19*/ { 0, 1, 33,}, + /* 20*/ { 32, 33,}, + /* 21*/ { 0, 1,}, + /* 22*/ { 32, 33,}, + /* 23*/ { 25, 27, 29, 32, 33,}, + /* 24*/ { 25, 27, 31,}, + /* 25*/ { 23, 24, 31,}, + /* 26*/ { 29, 33,}, + /* 27*/ { 2, 23, 24, 33,}, + /* 28*/ { 2, 31, 33,}, + /* 29*/ { 23, 26, 32, 33,}, + /* 30*/ { 1, 8, 32, 33,}, + /* 31*/ { 0, 24, 25, 28, 32, 33,}, + /* 32*/ { 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31, 33,}, + /* 33*/ { 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32,}, + }; + +// clang-format on diff --git a/examples/CppCon2021/graphs/ospf-graph.hpp b/examples/CppCon2021/graphs/ospf-graph.hpp new file mode 100644 index 0000000..f0cbcb6 --- /dev/null +++ b/examples/CppCon2021/graphs/ospf-graph.hpp @@ -0,0 +1,205 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// +#pragma once + +// Data and graph from OSPF example in Boost Graph Library book + +#include +#include +#include + +// clang-format off + +std::vector ospf_vertices { + "RT1", + "RT2", + "RT3", + "RT4", + "RT5", + "RT6", + "RT7", + "RT8", + "RT9", + "RT10", + "RT11", + "RT12", + "N1", + "N2", + "N3", + "N4", + "N6", + "N7", + "N8", + "N9", + "N10", + "N11", + "N12", + "N13", + "N14", + "N15", + "H1", +}; + +std::vector> ospf_edges { + {"RT1", "N1", 3}, + {"RT1", "N3", 1}, + {"RT2", "N2", 3}, + {"RT2", "N3", 1}, + {"RT3", "RT6", 8}, + {"RT3", "N3", 1}, + {"RT3", "N4", 2}, + {"RT4", "N3", 1}, + {"RT4", "RT5", 8}, + {"RT5", "RT4", 8}, + {"RT5", "RT6", 7}, + {"RT5", "RT7", 6}, + {"RT5", "N12", 8}, + {"RT5", "N13", 8}, + {"RT5", "N14", 8}, + {"RT6", "RT3", 6}, + {"RT6", "RT5", 6}, + {"RT6", "RT10", 7}, + {"RT7", "RT5", 6}, + {"RT7", "N6", 1}, + {"RT7", "N12", 2}, + {"RT7", "N15", 9}, + {"RT8", "N6", 1}, + {"RT8", "N7", 4}, + {"RT9", "N9", 1}, + {"RT9", "N11", 3}, + {"RT10", "RT6", 5}, + {"RT10", "N6", 1}, + {"RT10", "N8", 3}, + {"RT11", "N8", 2}, + {"RT11", "N9", 1}, + {"RT12", "N9", 1}, + {"RT12", "N10", 2}, + {"RT12", "H1", 10}, + {"N3", "RT1", 0}, + {"N3", "RT2", 0}, + {"N3", "RT3", 0}, + {"N3", "RT4", 0}, + {"N6", "RT7", 0}, + {"N6", "RT8", 0}, + {"N6", "RT10", 0}, + {"N8", "RT10", 0}, + {"N8", "RT11", 0}, + {"N9", "RT9", 0}, + {"N9", "RT11", 0}, + {"N9", "RT12", 0}, + }; + +std::vector> ospf_index_edge_list { +{ 0, 12, 3}, +{ 0, 14, 1}, +{ 1, 13, 3}, +{ 1, 14, 1}, +{ 2, 5, 8}, +{ 2, 14, 1}, +{ 2, 15, 2}, +{ 3, 14, 1}, +{ 3, 4, 8}, +{ 4, 3, 8}, +{ 4, 5, 7}, +{ 4, 6, 6}, +{ 4, 22, 8}, +{ 4, 23, 8}, +{ 4, 24, 8}, +{ 5, 2, 6}, +{ 5, 4, 6}, +{ 5, 9, 7}, +{ 6, 4, 6}, +{ 6, 16, 1}, +{ 6, 22, 2}, +{ 6, 25, 9}, +{ 7, 16, 1}, +{ 7, 17, 4}, +{ 8, 19, 1}, +{ 8, 21, 3}, +{ 9, 5, 5}, +{ 9, 16, 1}, +{ 9, 18, 3}, +{ 10, 18, 2}, +{ 10, 19, 1}, +{ 11, 19, 1}, +{ 11, 20, 2}, +{ 11, 26, 10}, +{ 14, 0, 0}, +{ 14, 1, 0}, +{ 14, 2, 0}, +{ 14, 3, 0}, +{ 16, 6, 0}, +{ 16, 7, 0}, +{ 16, 9, 0}, +{ 18, 9, 0}, +{ 18, 10, 0}, +{ 19, 8, 0}, +{ 19, 10, 0}, +{ 19, 11, 0}, +}; + +std::vector>> ospf_index_adjacency_list { + /* 0 */ { { 12, 3 },{ 14, 1 },}, + /* 1 */ { { 13, 3 },{ 14, 1 },}, + /* 2 */ { { 5, 8 },{ 14, 1 },{ 15, 2 },}, + /* 3 */ { { 14, 1 },{ 4, 8 },}, + /* 4 */ { { 3, 8 },{ 5, 7 },{ 6, 6 },{ 22, 8 },{ 23, 8 },{ 24, 8 },}, + /* 5 */ { { 2, 6 },{ 4, 6 },{ 9, 7 },}, + /* 6 */ { { 4, 6 },{ 16, 1 },{ 22, 2 },{ 25, 9 },}, + /* 7 */ { { 16, 1 },{ 17, 4 },}, + /* 8 */ { { 19, 1 },{ 21, 3 },}, + /* 9 */ { { 5, 5 },{ 16, 1 },{ 18, 3 },}, + /* 10 */ { { 18, 2 },{ 19, 1 },}, + /* 11 */ { { 19, 1 },{ 20, 2 },{ 26, 10 },}, + /* 12 */ { }, + /* 13 */ { }, + /* 14 */ { { 0, 0 },{ 1, 0 },{ 2, 0 },{ 3, 0 },}, + /* 15 */ { }, + /* 16 */ { { 6, 0 },{ 7, 0 },{ 9, 0 },}, + /* 17 */ { }, + /* 18 */ { { 9, 0 },{ 10, 0 },}, + /* 19 */ { { 8, 0 },{ 10, 0 },{ 11, 0 },}, + /* 20 */ { }, + /* 21 */ { }, + /* 22 */ { }, + /* 23 */ { }, + /* 24 */ { }, + /* 25 */ { }, + /* 26 */ { }, +}; + +std::vector> ospf_shortest_path_distances { + { "RT1", 7 }, + { "RT2", 7 }, + { "RT3", 6 }, + { "RT4", 7 }, + { "RT5", 6 }, + { "RT6", 0 }, + { "RT7", 8 }, + { "RT8", 8 }, + { "RT9", 11 }, + { "RT10", 7 }, + { "RT11", 10 }, + { "RT12", 11 }, + { "N1", 10 }, + { "N2", 10 }, + { "N3", 7 }, + { "N4", 8 }, + { "N6", 8 }, + { "N7", 12 }, + { "N8", 10 }, + { "N9", 11 }, + { "N10", 13 }, + { "N11", 14 }, + { "N12", 10 }, + { "N13", 14 }, + { "N14", 14 }, + { "N15", 17 }, + { "H1", 21 }, +}; + +// clang-format on diff --git a/examples/CppCon2021/graphs/spice-graph.hpp b/examples/CppCon2021/graphs/spice-graph.hpp new file mode 100644 index 0000000..157fb0f --- /dev/null +++ b/examples/CppCon2021/graphs/spice-graph.hpp @@ -0,0 +1,129 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// + +// Data and graph from Spice example in Cppcon slides + +#include +#include +#include + +// clang-format off + +std::vector spice_vertices { + "GND", + "n0", + "Vdd", + "n1", + "out", + "n2", +}; + +std::vector spice_elements { + "C1", + "AC", + "R2", + "R0", + "R1", + "L1", + "C0", + "R3", +}; + +std::vector> spice_elements_values { + { "C1", 1.e-6 }, + { "AC", 3 }, + { "R2", 1.e4 }, + { "R0", 2.7e3 }, + { "R1", 3.3e4 }, + { "L1", 1.e-4 }, + { "C0", 10.e-9}, + { "R3", 1.e3 }, +}; + +std::vector> spice_edges { + { "n0", "n1", "C1" }, + { "Vdd", "GND", "AC" }, + { "n0", "Vdd", "R2" }, + { "n2", "Vdd", "R0" }, + { "GND", "n2", "R1" }, + { "n2", "Vout", "L1" }, + { "GND", "n0", "C0"}, + { "n2", "n1", "R3"}, +}; + +std::vector> spice_edges_values { + { "n0", "n1", "C1", 1.e-6 }, + { "Vdd", "GND", "AC", 3 }, + { "n0", "Vdd", "R2", 1.e4 }, + { "n2", "Vdd", "R0", 2.7e3 }, + { "GND", "n2", "R1", 3.3e4 }, + { "n2", "Vout", "L1", 1.e-4 }, + { "GND", "n0", "C0", 10.e-9}, + { "n2", "n1", "R3", 1.e3 }, +}; + +std::vector> spice_index_edge_list { + { 1, 3, 0 }, + { 2, 0, 1 }, + { 1, 2, 2 }, + { 5, 2, 3 }, + { 0, 5, 4 }, + { 5, 4, 5 }, + { 0, 1, 6 }, + { 5, 3, 7 }, +}; + +std::vector> spice_property_edge_list { + { 1, 3, "C1" }, + { 2, 0, "AC" }, + { 1, 2, "R2" }, + { 5, 2, "R0" }, + { 0, 5, "R1" }, + { 5, 4, "L1" }, + { 0, 1, "C0" }, + { 5, 3, "R3" }, +}; + +std::vector> spice_property_edge_list_values { + { 1, 3, "C1", 1.e-6 }, + { 1, 0, "AC", 3 }, + { 1, 2, "R2", 1.e4 }, + { 5, 2, "R0", 2.7e3 }, + { 0, 5, "R1", 3.3e4 }, + { 5, 4, "L1", 1.e-4 }, + { 0, 1, "C0", 10.e-9}, + { 5, 3, "R3", 1.e3 }, +}; + +std::vector>> spice_index_adjacency_list { + /* 0 */ { { 1, 6 }, { 5, 4 } }, + /* 1 */ { { 2, 2 }, { 3, 0 } }, + /* 2 */ { { 0, 1 } }, + /* 3 */ { }, + /* 4 */ { }, + /* 5 */ { { 2, 3 }, { 4, 5 }, { 3, 7 } }, +}; + +std::vector>> spice_property_adjacency_list { + /* 0 */ { { 1, "C0" }, { 5, "R1" }, }, + /* 1 */ { { 2, "R2" }, { 3, "C1" } }, + /* 2 */ { { 0, "AC" } }, + /* 3 */ { }, + /* 4 */ { }, + /* 5 */ { { 2, "R0" }, { 4, "L1" }, { 3, "R3" } }, +}; + +std::vector>> spice_property_adjacency_list_values { + /* 0 */ { { 1, "C0", 10.e-9 }, { 5, "R1", 3.3e4 } }, + /* 1 */ { { 2, "R2", 1.e-4 }, { 3, "C1", 1.e-6 } }, + /* 2 */ { { 0, "AC", 3 } }, + /* 3 */ { }, + /* 4 */ { }, + /* 5 */ { { 2, "R0", 2.7e3 }, { 4, "L1", 1.e-4 }, { 3, "R3", 1.e3 } }, +}; + +// clang-format on diff --git a/examples/CppCon2021/include/utilities.hpp b/examples/CppCon2021/include/utilities.hpp new file mode 100644 index 0000000..65ee0cd --- /dev/null +++ b/examples/CppCon2021/include/utilities.hpp @@ -0,0 +1,322 @@ +// +// Authors +// Copyright +// License +// No Warranty Disclaimer +// + +#include +#include +#include +#include + +/** + * Tuple utilities to get the "cdr" of a tuple (for our purposes) + */ + +template +auto nth_cdr(std::tuple t) { + return [&](std::index_sequence) { + return std::tuple{std::get(t)...}; + }(std::make_index_sequence()); +} + +template +auto props(std::tuple t) { + return nth_cdr<2>(t); +} + +template +auto graph_edge(std::tuple t) { + return nth_cdr<1>(t); +} + +/** + * Fill a plain graph from edge list + */ +template +void push_back_plain_fill(const EdgeList& edge_list, Adjacency& adj, bool directed, size_t idx) { + const size_t jdx = (idx + 1) % 2; + + for (auto&& e : edge_list) { + + if (idx == 0) { + std::apply([&](size_t u, size_t v) { adj[u].emplace_back(v); }, e); + if (!directed) { + std::apply([&](size_t u, size_t v) { adj[v].emplace_back(u); }, e); + } + } else { + std::apply([&](size_t u, size_t v) { adj[v].emplace_back(u); }, e); + if (!directed) { + std::apply([&](size_t u, size_t v) { adj[u].emplace_back(v); }, e); + } + } + } +} + +/** + * Fill a non-plain graph from edge list + */ +template +void push_back_fill(const EdgeList& edge_list, Adjacency& adj, bool directed, size_t idx) { + const size_t jdx = (idx + 1) % 2; + + for (auto&& e : edge_list) { + + if (idx == 0) { + std::apply([&](size_t u, size_t v, auto... props) { adj[u].emplace_back(v, props...); }, e); + if (!directed) { + std::apply([&](size_t u, size_t v, auto... props) { adj[v].emplace_back(u, props...); }, e); + } + } else { + std::apply([&](size_t u, size_t v, auto... props) { adj[v].emplace_back(u, props...); }, e); + if (!directed) { + std::apply([&](size_t u, size_t v, auto... props) { adj[u].emplace_back(v, props...); }, e); + } + } + } +} + +/** + * Make a map from data to the index value of each element in its container + */ +template +auto make_index_map(const R& range) { + using value_type = std::ranges::range_value_t; + + std::map the_map; + for (size_t i = 0; i < size(range); ++i) { + the_map[range[i]] = i; + } + return the_map; +} + +/** + * Make an edge list with properties copied from original data, e.g., vector> + */ +//template