Conversation
…larity\n\nPhase 1: in_edges CPO, in_degree CPO, outgoing aliases (out_edges,\nout_degree, find_out_edge), type aliases (in_edge_range_t, in_edge_iterator_t,\nin_edge_t, out_edge_range_t, out_edge_iterator_t, out_edge_t).\n\nPhase 2: find_in_edge and contains_in_edge CPOs with 3-tier resolution\n(member/ADL/default). Default tiers delegate to outgoing edge operations\nin reverse direction. Added has_in_degree, has_find_in_edge,\nhas_contains_in_edge traits.\n\nPhase 3: out_edge_range, in_edge_range, bidirectional_adjacency_list,\nindex_bidirectional_adjacency_list concepts. Updated graph.hpp with full\nre-exports for all Phases 1-3 items.\n\nRenamed for directional clarity:\n- vertex_edge_range concept -> out_edge_range\n- in_vertex_edge_range concept -> in_edge_range\n- out_vertex_edge_range_t -> out_edge_range_t\n- in_vertex_edge_range_t -> in_edge_range_t\n- out_vertex_edge_iterator_t -> out_edge_iterator_t\n- in_vertex_edge_iterator_t -> in_edge_iterator_t\n\nAll 4342 tests pass."
The explicit directional names (out_edges, out_degree, find_out_edge, out_edge_range_t, out_edge_iterator_t, out_edge_t) are now the primary CPO definitions and type aliases. The shorter names (edges, degree, find_vertex_edge, vertex_edge_range_t, vertex_edge_iterator_t, edge_t) become convenience aliases via inline constexpr auto&. Code changes: - graph_cpo.hpp: swap primary/alias for 3 CPOs + 6 type aliases - adjacency_list_concepts.hpp: use out_edges in concept definitions, fix concept ordering (ordered_vertex_edges after adjacency_list) Documentation updates: - cpo-reference.md, adjacency-lists.md, type-aliases.md, adjacency-list-interface.md, cpo-order.md, coding-guidelines.md - agents/incoming_edges_design.md, incoming_edges_plan.md All 4342 tests pass.
… alias Follows the same pattern as out_edges/edges, out_degree/degree, and find_out_edge/find_vertex_edge: the explicit directional name is the primary definition, the shorter name is a convenience alias. - graph_cpo.hpp: contains_out_edge is primary _fn instance, contains_edge is inline constexpr auto& alias - graph.hpp: re-export both contains_out_edge and contains_edge - Updated cpo-reference.md, cpo-order.md All 4342 tests pass.
The plural form better reflects the CPO's semantics: it checks whether the graph has any edges (plural), not a specific edge. Internal dispatch unchanged: the CPO still resolves g.has_edge() member and ADL has_edge(g) customization points on containers. All 4342 tests pass.
Add in_edges() ADL friends to base_undirected_adjacency_list that forward to the same edge ranges as edges(), making undirected graphs model bidirectional_adjacency_list at zero cost. in_degree is handled automatically by the CPO default tier (size(in_edges(g, u))). find_in_edge and contains_in_edge also work correctly via their default tiers which delegate to find_vertex_edge/edges with swapped arguments. 10 new test cases covering: - Concept satisfaction (bidirectional + index_bidirectional) - in_edges == edges identity per vertex - in_degree == degree for all vertices - find_in_edge with vertex ids, descriptors, and mixed - contains_in_edge positive and negative cases - Star and triangle graph topologies All 4352 tests pass.
Phase 5 now renames existing incidence.hpp/neighbors.hpp to out_incidence.hpp/out_neighbors.hpp (primary), with thin forwarding headers as aliases. in_incidence.hpp/in_neighbors.hpp are the new incoming-edge views. Phase 6 similarly renames adaptor instances to out_* primary, incidence/neighbors as aliases, then adds in_* adaptor closures.
Keep out_*, in_*, and alias views all in the same incidence.hpp and neighbors.hpp files. No file renames or forwarding headers needed. Mirrors how graph_cpo.hpp keeps out_edges, in_edges, and edges alias together.
Introduce out_edge_accessor / in_edge_accessor in Phase 5 as a single parameterization point. Views (incidence, neighbors) gain an Accessor template parameter defaulting to out_edge_accessor — no class duplication. Phase 7 reuses the same accessor types for BFS/DFS/topo-sort, eliminating the need for a separate edge_accessor.hpp in that phase.
Introduce out_edge_accessor and in_edge_accessor as stateless policy structs that bundle edge-range fetching, neighbor-id extraction, and neighbor-vertex access. All four incidence view classes and all four neighbors view classes gain an Accessor template parameter (defaulting to out_edge_accessor) so existing code is source-compatible. New factory functions: - out_incidence / in_incidence / basic_out_incidence / basic_in_incidence - out_neighbors / in_neighbors / basic_out_neighbors / basic_in_neighbors Files changed: - NEW include/graph/views/edge_accessor.hpp - MOD include/graph/views/incidence.hpp (parameterized + factories) - MOD include/graph/views/neighbors.hpp (parameterized + factories) - MOD include/graph/graph.hpp (include edge_accessor.hpp) - NEW tests/views/test_in_incidence.cpp (14 test sections) - NEW tests/views/test_in_neighbors.cpp (14 test sections) - MOD tests/views/CMakeLists.txt (register new tests) All 4352 tests pass (461 views tests, 6238 assertions).
Add incoming adaptor closures so pipe syntax works for in_edges: g | in_incidence(uid) g | in_incidence(uid, evf) g | in_neighbors(uid) g | in_neighbors(uid, vvf) g | basic_in_incidence(uid) g | basic_in_neighbors(uid) Outgoing adaptors gain explicit out_* names via type aliases: out_incidence_adaptor_fn = incidence_adaptor_fn (etc.) Short names are now inline constexpr references to out_* objects: incidence = out_incidence, neighbors = out_neighbors basic_incidence = basic_out_incidence, basic_neighbors = basic_out_neighbors Files changed: - MOD include/graph/views/adaptors.hpp (8 new adaptor types + instances) - MOD include/graph/views/neighbors.hpp (static_cast fix for Clang) - MOD include/graph/views/edge_accessor.hpp (minor formatting) - MOD tests/views/test_adaptors.cpp (16 new test cases, 25 assertions) All 4352 tests pass on both GCC and Clang.
Complete Phase 8 of incoming edges plan — direction-aware edge_descriptor, in_edges ADL friends, and all CPO fixes for bidirectional dynamic_graph. Direction tag design (Option A): - Added out_edge_tag / in_edge_tag in descriptor.hpp - EdgeDirection 3rd template parameter on edge_descriptor (default out_edge_tag) - is_in_edge / is_out_edge static constexpr members - requires constraints on tag-dependent member functions: - source_id(vertex_data) requires(is_in_edge) - target_id(vertex_data) split into requires(is_in_edge) / requires(is_out_edge) Mirror principle (A1) implementation: - source_id(vertex_data): navigates .in_edges() for actual source (in-edge only) - target_id(vertex_data): trivial return for in-edges, navigates .edges() for out-edges - underlying_value/inner_value: direction-aware container selection Infrastructure changes: - edge_descriptor_view: EdgeDirection parameter, enable_borrowed_range for 3-param - descriptor_traits: all 11 specializations updated, is_in_edge_descriptor trait - edge_cpo.hpp: source_id tier 4 with if constexpr (is_in_edge) + fallback safety - graph_cpo.hpp: _wrap_in_edge helper in _in_edges namespace - dynamic_graph.hpp: in_edges(g,u) ADL friends with in_edge_tag descriptors, direction-aware edge_value ADL friend Bidirectional template parameter (steps 8.1-8.4): - Propagated through all classes, 27 traits files, all test files - dynamic_vertex_bidir_base with conditional in_edges_ storage - load_edges populates both containers when Bidirectional - _has_default_uid CPO fix for in_edges 4391/4391 tests pass on GCC-15 and Clang, zero regressions.
- Add include/graph/views/transpose.hpp: zero-cost bidirectional graph adaptor that swaps edges<->in_edges and target<->source via ADL friends. Documented limitation for forward-iterator containers (CPO tier-1 bypass). - Add kosaraju(G&&, Component&) overload in connected_components.hpp, constrained on index_bidirectional_adjacency_list. Uses manual iterative stack-based DFS with in_edges/source_id for the second pass — works with ALL container types (vov + vol). O(V+E) with no transpose overhead. - Add tests/views/test_transpose.cpp: 9 tests verifying edge/degree swap, double-transpose identity, edge_value forwarding, edge cases. - Add tests/algorithms/test_scc_bidirectional.cpp: 14 tests covering single vertex, cycles, multi-SCC, DAG, self-loops, weighted, disconnected, and agreement with the two-graph overload — tested on both vov and vol. - Update graph.hpp to include transpose.hpp. - Update CMakeLists for both test targets. 4405/4405 tests pass on GCC-15 and Clang."
Phase 7 — BFS/DFS/topological sort Accessor parameterization: - Add Accessor template param (default out_edge_accessor) to all BFS, DFS, and topological sort view classes and factory functions - Fix in_edge_accessor::neighbor() — was calling adj_list::source() which returns owning vertex for in-edge descriptors; now uses find_vertex(g, source_id(g, e)) for correct resolution - Add 22 new tests in test_reverse_traversal.cpp covering forward and reverse traversal for all search view types Phase 10 — Documentation: - Create docs/user-guide/bidirectional-access.md tutorial guide - Update 11 existing doc files: index, getting-started, views, algorithms, containers, concepts, cpo-reference, type-aliases, adjacency-list-interface, cpo-implementation, README - Add CHANGELOG entry for full bidirectional edge access feature - Document bidirectional_adjacency_list concept, in_edge_* type aliases, Bidirectional template parameter, in_incidence/in_neighbors views, and Accessor parameter on search views 4405/4405 tests passing.
…directed support migration-from-v2.md: - Add undirected_adjacency_list section with template signature, complexity table, iterator invalidation rules, usage example, and when-to-use guidance - Add Bidirectional Graphs section covering dynamic_graph<...,true,true,...>, incoming-edge CPO table (in_edges, in_degree, find_in_edge, contains_in_edge, source_id), in_incidence/in_neighbors view table with structured-binding yields, and algorithms note (SCC, reverse BFS/DFS) - Add bidirectional graphs row to Key Changes at a Glance table - Add migration checklist item for predecessor/incoming-edge queries docs/status/coverage.md: - Regenerate from clean build (2026-02-22, 4405 tests, 96.0% line / 91.8% func) - Add edge_accessor.hpp and transpose.hpp (new files in views/) - Update all per-file line and function counts cmake/CodeCoverage.cmake: - Write raw data to coverage_raw.info then filter into coverage.info to avoid overwriting in place - Add --ignore-errors unused to lcov --remove to fix failure with lcov >= 2.x when the /usr/* exclude pattern matches nothing
- Views: 7 → 10 (added in_incidence, in_neighbors, transpose) - Trait combinations: 26 → 27 - Test count: 4261 → 4405 (2026-02-19 → 2026-02-23) - Coverage: 96.0% line / 91.8% function - Connected components row now notes Kosaraju SCC + test_scc_bidirectional.cpp - Removed stale note about README using graph::graph target (already graph::graph3)
…- Add in_edges_type derived alias fallback rule (detected_or_t)\n- Fix Phase 2 sequencing: temporary constructibility dispatch\n- Fix Phase 4c: in_edges_type omission from standard traits\n- Add compile-time safety risk row for fallback aliases\n- Add Definition of Done section\n- Remove stale Sourced=true and uniform-mode references"
…lan with precise file/line references, safety mitigations,\ntemporary bridge patterns, and status tracker. Derived from\ndynamic_edge_refactor_strategy.md."
11 issues identified in deep cross-review of plan vs source code: - 4a.4: CRITICAL — remove dynamic_edge_source from dynamic_out_edge base list after Sourced=false empty spec is deleted; promote surviving Sourced=false specs to primary template (not just 'keep') - 4a.5: drop Sourced arg from dynamic_in_edge's dynamic_edge_value base - 4b.2: call out dynamic_vertex_base internal edge_type/edges_type aliases (L721-722) that also need Sourced removed - 4b.4: add edge_allocator_type audit + in_edge_allocator_type alias for non-uniform bidirectional mode - 4b.6 (new): edge_value direction-aware friend function needs Sourced removed and type-based dispatch check - 4d.1: clarify that all 18 emplace_edge calls across 3 load_edges paths must be audited; Sourced=false branch promoted, Sourced=true deleted - 4g: Phase 4g build gate replaced with example-target build (no library-only CMake target exists) - Overview: add test_dynamic_edge_comparison.cpp to Files of Interest - 5.1.1: warn that Sourced→Bidirectional at position 5 is a semantic change, not just a rename; callers passing true/false must be audited - 5.2: add detailed test_dynamic_edge_comparison.cpp migration steps - Risk table: add row for missing library-only CMake target"
Purely additive changes to dynamic_graph.hpp (198 insertions, 0 deletions):
1. Forward declaration for dynamic_in_edge<EV,VV,GV,VId,Bidirectional,Traits>
(6 params, no Sourced) after the dynamic_edge forward declaration.
2. graph::container::detail namespace with detection-idiom helpers:
- detected_or_t<Default, Op, Args...> — returns Op<Args...> if
well-formed, otherwise Default (no Boost dependency)
- detect_in_edge_type<T> — detects T::in_edge_type
- detect_in_edges_type<T> — detects T::in_edges_type
3. dynamic_in_edge_source<EV,VV,GV,VId,Bidirectional,Traits> — temporary
unconditional source-id base (no Sourced param). Replaces in Phase 4a.
4. dynamic_in_edge — 2 specializations (EV!=void, EV=void):
- Primary: inherits dynamic_in_edge_source + dynamic_edge_value
(Sourced=true pass-through). Constructors: (src), (src,val), (src,val&&).
operator<=> and operator== compare by source_id.
- void specialization: inherits dynamic_in_edge_source only.
Constructor: (src). Same comparison operators.
5. In dynamic_graph_base: three new dead aliases (activated in Phase 2):
- out_edge_type = edge_type
- in_edge_type = detected_or_t<edge_type, detect_in_edge_type, Traits>
- in_edges_type = detected_or_t<edges_type, detect_in_edges_type, Traits>
- static_assert verifying non-uniform traits define both in_edge_type and
in_edges_type if either is defined.
6. std::hash<dynamic_in_edge<...>> — hashes source_id only.
Phase 1 gate: cmake --build build/linux-gcc-debug succeeded,
4405/4405 tests passed, zero failures."
Three changes to dynamic_graph.hpp (all existing tests still pass):
1. dynamic_vertex_bidir_base<..., true>: edges_type (in-edge storage) now
derived via detection idiom:
detail::detected_or_t<Traits::edges_type, detect_in_edges_type, Traits>
Falls back to Traits::edges_type for all 27 standard traits — zero
runtime behaviour change. Enables future non-uniform traits to supply
a distinct in_edges_type.
2. make_in_edge private helpers in dynamic_graph_base (temporary bridge,
removed in Phase 4d). Dispatch based on constructibility:
- if InEdge is constructible from (VId, VId [, Val]):
legacy path for edge_type when Sourced=true
- otherwise:
future path for dynamic_in_edge which takes (VId [, Val])
Two overloads: void-EV (no value) and non-void-EV (copy value).
3. All 6 in-edge emplace_edge sites across the 3 load_edges paths changed
from edge_type{src, tgt} to make_in_edge(src, tgt) (and analogously
for the value-bearing variant). Out-edge construction unchanged.
Phase 2 gate: cmake --build build/linux-gcc-debug succeeded,
4405/4405 tests passed, zero failures."
…s (Phase 3) - Renamed all dynamic_edge class declarations to dynamic_out_edge in dynamic_graph.hpp (80 occurrences: class decls, edge_type aliases, hash specializations) - Added deprecated alias 'using dynamic_edge = dynamic_out_edge<...>' for backward compatibility; will be removed in Phase 4 - Renamed forward declarations and edge_type aliases in all 27 traits headers from dynamic_edge to dynamic_out_edge - test_dynamic_edge_comparison.cpp continues to compile via the alias 4405/4405 tests pass
… 4→2 specs (Phase 4a) Phase 4a of the dynamic_edge refactor plan: - 4a.1: Remove bool Sourced from dynamic_edge_target template params - 4a.2: Remove bool Sourced from dynamic_edge_source, delete empty Sourced=false specialization (now unconditionally stores source_id) - 4a.3: Remove bool Sourced from dynamic_edge_value (both specs) - 4a.4: Collapse dynamic_out_edge from 4 to 2 specializations (keyed on EV only). dynamic_out_edge no longer inherits dynamic_edge_source. - 4a.5: Switch dynamic_in_edge bases from dynamic_in_edge_source to the repurposed dynamic_edge_source; delete dynamic_in_edge_source - 4a.6: Update dynamic_out_edge forward declaration (6 params) - 4a.7: Remove deprecated dynamic_edge alias; collapse 2 hash specs into 1 (hashes target_id only) Does NOT compile yet — vertex/graph classes still reference Sourced. Continues in Phase 4b.
- dynamic_vertex_bidir_base: Remove Sourced from both specializations; delete static_assert(Sourced, ...) in Bidirectional=true spec - dynamic_vertex_base: Remove Sourced from template params, bidir_base reference, and graph_type/vertex_type/edge_type aliases - dynamic_vertex: Remove Sourced from both specializations (primary and VV=void) including all type aliases and base class references - dynamic_graph_base: Remove Sourced from template params and all type aliases (graph_type, vertex_type, edge_type) - dynamic_graph: Remove Sourced from both specializations (primary and GV=void); remove 'constexpr ... sourced = Sourced' constant - Forward declarations: Remove 'bool Sourced = false' from dynamic_graph and 'bool Sourced' from dynamic_vertex - dynamic_adjacency_graph alias: Remove Traits::sourced - edge_value friend function: Verified no Sourced dependency (uses is_in_edge type trait for direction dispatch) Remaining Sourced references (to be addressed in later phases): - Lines 58-82: traits forward declarations (Phase 4c) - Lines 1311,1406,1444: if constexpr(Sourced) in load_edges (Phase 4d) Does NOT compile yet — traits files still pass Sourced.
- All 27 traits files (vofl, vol, vov, vod, vos, vous, vom, voum, dofl, dol, dov, dod, dos, dous, mofl, mol, mov, mod, mos, mous, mom, uofl, uol, uov, uod, uos, uous): * Remove 'bool Sourced' from forward declarations (6→5 params) * Remove 'bool Sourced = false' from struct template params * Remove 'static constexpr bool sourced = Sourced;' member * Remove Sourced from edge_type/vertex_type/graph_type aliases * Update comments referencing Sourced - dynamic_graph.hpp: Remove 'bool Sourced' from all 9 traits forward declarations (lines 58-82) Remaining Sourced references (Phase 4d): - Lines 1311,1406,1444: if constexpr(Sourced) in load_edges - Line 242: comment in dynamic_edge_source Does NOT compile yet — load_edges still references Sourced.
- All 3 load_edges paths (associative, sequential-forward, fallback):
* Remove if constexpr(Sourced) guards — collapse to unconditional path
* Out-edge construction: always edge_type(target_id [, value])
* In-edge construction: direct in_edge_type(source_id [, value]) guarded
by if constexpr(Bidirectional) — replaces make_in_edge bridge
- Delete make_in_edge helper (2 overloads, added in Phase 2)
27 insertions, 74 deletions. No Sourced references remain in code
(only 1 documentation comment at line 242 in dynamic_edge_source).
Does NOT compile yet — Phase 4e-4g pending.
- 4f: Hash specializations already Sourced-free (done in Phase 4a)
- 4g: Fixed example files that still used old 7-param dynamic_graph:
* dijkstra_clrs_example.cpp: Remove Sourced from dynamic_graph and
vol_graph_traits template args
* mst_usage_example.cpp: Remove Sourced from dynamic_graph and
vov_graph_traits template args
- Build verification: dijkstra_example and basic_usage compile cleanly
Phase 4 complete. Tests still reference Sourced (Phase 5).
…iases - All 27 tag structs: rename 'bool Sourced' -> 'bool Bidirectional = false' in template alias, update 5th traits arg to Bidirectional - graph_test_types struct: * Drop 7-param dynamic_graph (Sourced removed) -> 6-param * Remove sourced_void / sourced_int / sourced_all aliases * Add bidir_void / bidir_int / bidir_all aliases (Bidirectional=true) * Update doxygen comments - File header: update 'sourced' -> 'bidirectional' in description
…uniform coverage (Phase 5)
…ynamic_edge refs (Phase 6.2)
…rror paths + visitor)
pratzl
added a commit
that referenced
this pull request
Feb 26, 2026
Conflicts resolved: - adjacency_list_traits.hpp: kept const& on new find_in_edge concepts - edge_descriptor.hpp: kept origin/main's auto return types (not vertex_id params) - connected_components.hpp: kept const& on lambda param - bfs.hpp, dfs.hpp: kept const& on seed params in new factory functions - incidence.hpp, neighbors.hpp: kept const& on uid params including new in_* factories - transpose.hpp: kept const& on find_vertex uid params - test_dynamic_graph_integration.cpp: kept our static_assert block All 4 presets green (GCC/Clang debug/release, 4343-4344 tests, 0 failures).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add support for bidirectional graph (e.g. incoming edges)