diff --git a/D3128_Algorithms/tex/algorithms.tex b/D3128_Algorithms/tex/algorithms.tex index 3595754..e333560 100644 --- a/D3128_Algorithms/tex/algorithms.tex +++ b/D3128_Algorithms/tex/algorithms.tex @@ -148,12 +148,12 @@ \subsection{Edge Weight Concepts} is_arithmetic_v && strict_weak_order && assignable_from, - invoke_result_t>>>; + invoke_result_t>>>; // For exposition only template concept edge_weight_function = // e.g. weight(uv) - is_arithmetic_v>> && + is_arithmetic_v>> && basic_edge_weight_function -concept adjacency_list = vertex_range && // - targeted_edge_range && // - targeted_edge; +template +concept adjacency_list = requires(G& g, vertex_t u) { + { vertices(g) } -> vertex_range; + { out_edges(g, u) } -> out_edge_range; +}; + +template +concept index_adjacency_list = adjacency_list && + index_vertex_range; -template -concept index_adjacency_list = index_vertex_range && // - targeted_edge_range && // - targeted_edge; +template +concept bidirectional_adjacency_list = + adjacency_list && + requires(G& g, vertex_t u, in_edge_t ie) { + { in_edges(g, u) } -> in_edge_range; + { source_id(g, ie) } -> convertible_to>; + }; + +template +concept index_bidirectional_adjacency_list = + bidirectional_adjacency_list && + index_vertex_range; template -concept sourced_adjacency_list = vertex_range && // - targeted_edge_range && // - sourced_targeted_edge; +concept mapped_adjacency_list = adjacency_list && + mapped_vertex_range; template -concept sourced_index_adjacency_list = index_vertex_range && // - targeted_edge_range && // - sourced_targeted_edge; +concept mapped_bidirectional_adjacency_list = + bidirectional_adjacency_list && + mapped_vertex_range; diff --git a/D3130_Container_Interface/src/concepts_basic_adj_list.hpp b/D3130_Container_Interface/src/concepts_basic_adj_list.hpp deleted file mode 100644 index 0ff0211..0000000 --- a/D3130_Container_Interface/src/concepts_basic_adj_list.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// For exposition only - -template -concept basic_adjacency_list = vertex_range && // - basic_targeted_edge_range && // - targeted_edge; - -template -concept basic_index_adjacency_list = index_vertex_range && // - basic_targeted_edge_range && // - basic_targeted_edge; - -template -concept basic_sourced_adjacency_list = vertex_range && // - basic_targeted_edge_range && // - basic_sourced_targeted_edge; - -template -concept basic_sourced_index_adjacency_list = index_vertex_range && // - basic_targeted_edge_range && // - basic_sourced_targeted_edge; diff --git a/D3130_Container_Interface/src/concepts_edges.hpp b/D3130_Container_Interface/src/concepts_edges.hpp index c0bfe23..6718c69 100644 --- a/D3130_Container_Interface/src/concepts_edges.hpp +++ b/D3130_Container_Interface/src/concepts_edges.hpp @@ -1,16 +1,9 @@ // For exposition only -template -concept targeted_edge = requires(G&& g, edge_reference_t uv) { - target(g, uv); - target_id(g, uv); - }; - -template -concept sourced_edge = requires(G&& g, edge_reference_t uv) { - source(g, uv); - source_id(g, uv); - }; - -template -concept sourced_targeted_edge = targeted_edge && sourced_edge; +template +concept edge = requires(G& g, const E& e) { + source_id(g, e); + source(g, e); + target_id(g, e); + target(g, e); +}; diff --git a/D3130_Container_Interface/src/concepts_edges_before.hpp b/D3130_Container_Interface/src/concepts_edges_before.hpp deleted file mode 100644 index 16b8d18..0000000 --- a/D3130_Container_Interface/src/concepts_edges_before.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// For exposition only - -template -concept basic_targeted_edge = requires(G&& g, edge_reference_t uv) { target_id(g, uv); }; - -template -concept basic_sourced_edge = requires(G&& g, edge_reference_t uv) { source_id(g, uv); }; - -template -concept basic_sourced_targeted_edge = basic_targeted_edge && basic_sourced_edge; - -template -concept targeted_edge = basic_targeted_edge && // - requires(G&& g, edge_reference_t uv) { target(g, uv); }; - -template -concept sourced_edge = basic_sourced_edge && // - requires(G&& g, edge_reference_t uv) { source(g, uv); }; - -template -concept sourced_targeted_edge = targeted_edge && sourced_edge; diff --git a/D3130_Container_Interface/src/concepts_target_edge_range.hpp b/D3130_Container_Interface/src/concepts_target_edge_range.hpp index de2b860..38d666d 100644 --- a/D3130_Container_Interface/src/concepts_target_edge_range.hpp +++ b/D3130_Container_Interface/src/concepts_target_edge_range.hpp @@ -1,9 +1,9 @@ // For exposition only -template -concept targeted_edge_range = - basic_targeted_edge_range && - requires(G&& g, vertex_reference_t u, vertex_id_t uid) { - { edges(g, u) } -> forward_range; - { edges(g, uid) } -> forward_range; // implies call to find\_vertex(g,uid) - }; +template +concept out_edge_range = forward_range && + edge>; + +template +concept in_edge_range = forward_range && + edge>; diff --git a/D3130_Container_Interface/src/concepts_vertex_range.hpp b/D3130_Container_Interface/src/concepts_vertex_range.hpp index 36c7e77..e62f0c6 100644 --- a/D3130_Container_Interface/src/concepts_vertex_range.hpp +++ b/D3130_Container_Interface/src/concepts_vertex_range.hpp @@ -1,14 +1,28 @@ // For exposition only -template -concept _common_vertex_range = ranges::sized_range> && - requires(G&& g, vertex_iterator_t ui) { vertex_id(g, ui); }; +template +concept vertex = requires(G& g, const V& u, const vertex_id_t& uid) { + vertex_id(g, u); + find_vertex(g, uid); +}; + +template +concept vertex_range = forward_range && + sized_range && + vertex>>; -template -concept vertex_range = _common_vertex_range> && - forward_range>; +template +concept index_vertex_range = + integral> && + integral::storage_type> && + requires(G& g) { + { vertices(g) } -> vertex_range; + }; -template -concept index_vertex_range = _common_vertex_range> && - random_access_range> && - integral>; +template +concept mapped_vertex_range = + !index_vertex_range && + requires(G& g, const vertex_id_t& uid) { + { vertices(g) } -> forward_range; + find_vertex(g, uid); + }; diff --git a/D3130_Container_Interface/src/descriptor.hpp b/D3130_Container_Interface/src/descriptor.hpp index 5b547ad..87060a6 100644 --- a/D3130_Container_Interface/src/descriptor.hpp +++ b/D3130_Container_Interface/src/descriptor.hpp @@ -1,129 +1,81 @@ -template -class descriptor { -public: - using inner_iterator = InnerIter; - using inner_value_type = iter_value_t; - // preserve inner value constness based on inner\_iterator - using inner_reference = - conditional_t())>>, - add_lvalue_reference_t>, - add_lvalue_reference_t>; - using inner_pointer = - conditional_t())>>, - add_pointer_t>, - add_pointer_t>; - - // Determine if this an index-based or iterator-based descriptor - using id_type = ptrdiff_t; - using value_type = conditional_t, id_type, inner_iterator>; - - // Honor the const/non-const contract for the value type - using pointer = std::add_pointer_t; - using const_pointer = std::add_pointer_t>; - - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - using difference_type = std::iter_difference_t; - - constexpr descriptor() = default; - constexpr descriptor(const descriptor&) = default; - constexpr descriptor(descriptor&&) = default; - constexpr ~descriptor() noexcept = default; - - constexpr descriptor(InnerIter first, InnerIter it) : begin_(first); - constexpr descriptor(InnerIter first, ptrdiff_t id); - - // for testing - template - requires is_convertible_v, InnerIter> - constexpr descriptor(R& r, inner_iterator it). - - template - requires is_convertible_v, InnerIter> - constexpr descriptor(R& r, std::ptrdiff_t id = 0); - - constexpr descriptor& operator=(const descriptor&) = default; - constexpr descriptor& operator=(descriptor&&) = default; - - // Properies +// For exposition only + +struct out_edge_tag {}; +struct in_edge_tag {}; + +template +class vertex_descriptor { public: - constexpr value_type& value() const noexcept; + using iterator_type = VertexIter; + using value_type = iter_value_t; + + // index for random-access, iterator for bidirectional (for exposition only) + using storage_type = + conditional_t, size_t, VertexIter>; + + constexpr vertex_descriptor() = default; + constexpr explicit vertex_descriptor(storage_type val) noexcept; - constexpr inner_iterator get_inner_iterator() const; + // Properties + constexpr storage_type value() const noexcept; + constexpr decltype(auto) vertex_id() const noexcept; - [[nodiscard]] constexpr inner_reference inner_value() noexcept; - [[nodiscard]] constexpr inner_reference inner_value() const noexcept; + template + constexpr decltype(auto) underlying_value(Container& c) const noexcept; - // Operators + template + constexpr decltype(auto) inner_value(Container& c) const noexcept; + + // Iterator-like interface + constexpr vertex_descriptor& operator++() noexcept; + constexpr vertex_descriptor operator++(int) noexcept; + + // Comparison + constexpr auto operator<=>(const vertex_descriptor&) const noexcept = default; + constexpr bool operator==(const vertex_descriptor&) const noexcept = default; + +private: + storage_type storage_ = storage_type(); // for exposition only +}; + + +template +class edge_descriptor { public: - // - // dereference - // - // Note: range concept requirement: decltype(*descriptor) == value\_type\& - [[nodiscard]] constexpr reference operator*() noexcept; - [[nodiscard]] constexpr const_reference operator*() const noexcept; - - [[nodiscard]] constexpr pointer operator->() noexcept; - [[nodiscard]] constexpr const_pointer operator->() const noexcept; - - // - // operator ++ += + - // - constexpr descriptor& operator++(); - constexpr descriptor operator++(int); - - constexpr descriptor& operator+=(iter_difference_t n) - requires random_access_iterator; - constexpr descriptor operator+(iter_difference_t n) const - requires random_access_iterator; - - // - // operator -- -= - - // - constexpr descriptor& operator--() - requires bidirectional_iterator; - constexpr descriptor operator--(int) - requires bidirectional_iterator; - - constexpr descriptor& operator-=(iter_difference_t n) - requires random_access_iterator; - constexpr descriptor operator-(iter_difference_t n) const - requires random_access_iterator; - - template - constexpr iter_difference_t operator-(const descriptor& rhs) const - requires random_access_iterator; - - // - // operator [] - // - constexpr inner_reference operator[](iter_difference_t n) const - requires random_access_iterator; - - // - // operators ==, <=> - // - constexpr bool operator==(const descriptor& rhs) const noexcept; - - template - requires std::equality_comparable_with - constexpr bool operator==(const descriptor& rhs) const noexcept; - - // for testing; useful in general? - template - requires std::equality_comparable_with - constexpr bool operator==(const InnerIter2& rhs) const noexcept; - - constexpr auto operator<=>(const descriptor& rhs) const noexcept - requires std::integral || std::random_access_iterator; - - template - requires std::three_way_comparable_with && - (std::integral || random_access_iterator) - constexpr auto operator<=>(const descriptor& rhs) const noexcept; + using edge_iterator_type = EdgeIter; + using vertex_iterator_type = VertexIter; + using vertex_desc = vertex_descriptor; + using edge_direction = EdgeDirection; + + static constexpr bool is_in_edge = is_same_v; + static constexpr bool is_out_edge = is_same_v; + + // index for random-access, iterator for forward (for exposition only) + using edge_storage_type = + conditional_t, size_t, EdgeIter>; + + constexpr edge_descriptor() = default; + constexpr edge_descriptor(edge_storage_type edge_val, + vertex_desc source) noexcept; + + // Properties + constexpr edge_storage_type value() const noexcept; + constexpr vertex_desc source() const noexcept; + constexpr decltype(auto) source_id() const noexcept; + + template + constexpr auto target_id(const VertexData& vd) const noexcept; + + template + constexpr decltype(auto) underlying_value(VertexData& vd) const noexcept; + + // Comparison + constexpr auto operator<=>(const edge_descriptor&) const noexcept = default; + constexpr bool operator==(const edge_descriptor&) const noexcept = default; private: - value_type value_ = value_type(); // index or iterator (for exposition only) - inner_iterator begin_ = inner_iterator(); // begin of the inner range (for exposition only) + edge_storage_type edge_storage_ = edge_storage_type(); // for exposition only + vertex_desc source_ = vertex_desc(); // for exposition only }; diff --git a/D3130_Container_Interface/src/descriptor_traits.hpp b/D3130_Container_Interface/src/descriptor_traits.hpp new file mode 100644 index 0000000..50db51d --- /dev/null +++ b/D3130_Container_Interface/src/descriptor_traits.hpp @@ -0,0 +1,29 @@ +// For exposition only + +template struct is_vertex_descriptor : false_type {}; +template struct is_edge_descriptor : false_type {}; +template struct is_descriptor : false_type {}; + +// Specializations for vertex\_descriptor +template +struct is_vertex_descriptor> : true_type {}; +template +struct is_descriptor> : true_type {}; + +// Specializations for edge\_descriptor +template +struct is_edge_descriptor> + : true_type {}; +template +struct is_descriptor> + : true_type {}; + +// Helper variable templates +template +inline constexpr bool is_vertex_descriptor_v = is_vertex_descriptor>::value; + +template +inline constexpr bool is_edge_descriptor_v = is_edge_descriptor>::value; + +template +inline constexpr bool is_descriptor_v = is_descriptor>::value; diff --git a/D3130_Container_Interface/src/descriptor_view.hpp b/D3130_Container_Interface/src/descriptor_view.hpp index 117df4a..46ab7fc 100644 --- a/D3130_Container_Interface/src/descriptor_view.hpp +++ b/D3130_Container_Interface/src/descriptor_view.hpp @@ -1,11 +1,44 @@ -template -constexpr auto descriptor_view(R&& r); +// For exposition only -template -using descriptor_view_t = decltype(descriptor_view(declval())); +template +class vertex_descriptor_view : public view_interface> { +public: + using vertex_desc = vertex_descriptor; + using storage_type = typename vertex_desc::storage_type; + using underlying_iterator = VertexIter; -template -constexpr auto descriptor_subrange_view(R&& rng, R&& subrng); + class iterator; // forward iterator yielding vertex\_desc by value + using const_iterator = iterator; -template -using descriptor_subrange_view_t = decltype(descriptor_subrange_view(declval(), declval())); + constexpr vertex_descriptor_view() = default; + + template + constexpr explicit vertex_descriptor_view(Container& c); + + constexpr iterator begin() const noexcept; + constexpr iterator end() const noexcept; + constexpr size_t size() const noexcept; +}; + + +template +class edge_descriptor_view + : public view_interface> { +public: + using edge_desc = edge_descriptor; + using vertex_desc = vertex_descriptor; + + class iterator; // forward iterator yielding edge\_desc by value + using const_iterator = iterator; + + constexpr edge_descriptor_view() = default; + + template + constexpr edge_descriptor_view(Container& c, vertex_desc source); + + constexpr iterator begin() const noexcept; + constexpr iterator end() const noexcept; + constexpr size_t size() const noexcept; +}; diff --git a/D3130_Container_Interface/src/edgelist_concepts.hpp b/D3130_Container_Interface/src/edgelist_concepts.hpp index 8e0a07b..1cfb8f5 100644 --- a/D3130_Container_Interface/src/edgelist_concepts.hpp +++ b/D3130_Container_Interface/src/edgelist_concepts.hpp @@ -1,20 +1,28 @@ -template // For exposition only -concept sourced_edgelist = ranges::input_range && - !ranges::range> && - requires(range_value_t e) { - { source_id(e) }; - { target_id(e) } -> same_as; - }; +// For exposition only +namespace std::graph::edge_list { -template // For exposition only -concept sourced_index_edgelist = sourced_edgelist && - requires(range_value_t e) { - { source_id(e) } -> integral; - { target_id(e) } -> integral; // redundant, for clarity - }; +template +concept basic_sourced_edgelist = + ranges::input_range && + !ranges::range> && + requires(EL& el, ranges::range_value_t uv) { + { source_id(el, uv) }; + { target_id(el, uv) } -> convertible_to; + }; -template // For exposition only -concept has_edge_value = sourced_edgelist && // - requires(range_value_t e) { - { edge_value(e) }; - }; +template +concept basic_sourced_index_edgelist = + basic_sourced_edgelist && + requires(EL& el, ranges::range_value_t uv) { + { source_id(el, uv) } -> integral; + { target_id(el, uv) } -> integral; + }; + +template +concept has_edge_value = + basic_sourced_edgelist && + requires(EL& el, ranges::range_value_t uv) { + { edge_value(el, uv) }; + }; + +} // namespace std::graph::edge\_list diff --git a/D3130_Container_Interface/src/edgelist_types.hpp b/D3130_Container_Interface/src/edgelist_types.hpp index 3e817c3..a450e9f 100644 --- a/D3130_Container_Interface/src/edgelist_types.hpp +++ b/D3130_Container_Interface/src/edgelist_types.hpp @@ -1,17 +1,28 @@ -template // For exposition only +// For exposition only +namespace std::graph::edge_list { + +template using edge_range_t = EL; -template // For exposition only +template using edge_iterator_t = ranges::iterator_t>; -template // For exposition only -using edge_t = range_value_t>; +template +using edge_t = ranges::range_value_t>; + +template +using edge_value_t = + remove_cvref_t&>(), + declval>>()))>; -template // For exposition only -using edge_reference_t = ranges::range_reference_t>; +template +using vertex_id_t = + remove_cvref_t&>(), + declval>>()))>; -template // For exposition only -using edge_value_t = decltype(edge_value(declval>>())); +template // exposition only +using raw_vertex_id_t = + decltype(source_id(declval&>(), + declval>>())); -template // For exposition only -using vertex_id_t = decltype(source_id(declval>>())); +} // namespace std::graph::edge\_list diff --git a/D3130_Container_Interface/tex/container_interface.tex b/D3130_Container_Interface/tex/container_interface.tex index b73ec02..5282e31 100644 --- a/D3130_Container_Interface/tex/container_interface.tex +++ b/D3130_Container_Interface/tex/container_interface.tex @@ -6,7 +6,16 @@ \section{Graph Container Interface} The Graph Container Interface (GCI) defines the primitive concepts, traits, types and functions used to define and access adjacency lists (aka graphs) and edgelists, no matter their internal design and organization. For instance, an adjacency list can be a vector of lists from standard containers, CSR-based graph and adjacency matrix. Likewise, an edgelist can be a range of edges from a standard container or externally defined edge types, -provided they have a source\_id, target\_id and optional edge\_value. +provided they have a source\_id, target\_id and optional edge\_value. + +The GCI covers two peer abstract data types, each with its own namespace: +\begin{itemize} + \item \tcode{std::graph::adj\_list} --- concepts, type aliases, CPOs and descriptors for adjacency lists. + \item \tcode{std::graph::edge\_list} --- concepts, type aliases and CPOs for edge lists. +\end{itemize} +All adjacency list symbols are additionally re-exported into \tcode{std::graph} for convenience. Edge list +symbols are \emph{not} re-exported because several names (notably \tcode{vertex\_id\_t}) have different +definitions in each namespace and cannot coexist at the root level. If there is a desire to use the algorithms against externally defined data structures, the GCI exposes its functions as customization points to be overridden as needed. Likewise, externally defined algorithms can be used to operate on other data structures that meet the GCI requirements. @@ -27,79 +36,84 @@ \section{Graph Container Interface} \section{Adjacency List Interface} +\subsection{Namespace} +All adjacency list concepts, type aliases, CPOs and descriptors are defined in the \tcode{std::graph::adj\_list} +namespace. Every symbol is also re-exported into \tcode{std::graph} via \tcode{using} declarations so that +callers who include \tcode{} can write \tcode{graph::vertices(g)} rather than +\tcode{graph::adj\_list::vertices(g)}. Code that wishes to be explicit about the choice of abstract data +type, or that uses both an adjacency list and an edge list simultaneously, may qualify names with the +\tcode{graph::adj\_list::} prefix. + \subsection{Concepts} -This section describes the concepts to describe the adjacency lists used for graphs in the Graph Library. There are -a couple of qualifiers that are used in concept names. +This section describes the concepts used for adjacency lists in the Graph Library. Three qualifiers are used in concept names. \begin{itemize} - \item \textbf{index} where the vertex range is random-access and the vertex id is integral. - \item \textbf{sourced} where an edge has a source id. + \item \textbf{index} — the vertex range is random-access and the vertex id is an integral type. + \item \textbf{mapped} — vertices are stored in an associative container (e.g.\tcode{map} or \tcode{unordered_map}); vertex lookup uses \tcode{find_vertex(g, uid)}. + \item \textbf{bidirectional} — the graph exposes in-edges via \tcode{in_edges(g, u)} in addition to out-edges. \end{itemize} \emph{While we believe the use of concepts is appropriate for graphs as a range-of-ranges, we are marking them as "For exposition only" until we have consensus of whether they belong in the standard or not.} \subsubsection{Edge Concepts} -The types of edges that can occur in a graph are described with the edges concepts. +The \tcode{edge} concept unifies all edge types in a single concept: an edge must provide \tcode{source_id}, +\tcode{source}, \tcode{target_id}, and \tcode{target} access given the graph. {\small \lstinputlisting{D3130_Container_Interface/src/concepts_edges.hpp} } -Return types are not validated in order to provide flexibility, and because it offers little value. Let's look at the options using -\tcode{target_id(g,uv)} as an example. - -\begin{lstlisting} - { target_id(g,uv) } -> integral; -\end{lstlisting} -This may seem obvious on first glance to some, but doing so limits us to integral ids. Graphs can use non-integral vertex id types for -vertices stored in a \tcode{map} or \tcode{unordered_map}. -It can be more efficient to simply run an algorithm on the existing graph rather than to copy it into a “high performance” graph data -structure just to run the algorithm because the copying operation can far outweigh the cost of running the algorithm on the native data -structures, even when those data structures offer $\mathcal{O}(log(n))$ lookup on vertices. While we're not proposing algorithms that -can do this today, the library needs to keep the door open to such algorithms in the future as well as supporting such algorithms -outside the standard library. - -\begin{lstlisting} - { target_id(g,uv) } -> vertex_id_t; -\end{lstlisting} -This is better than the previous example. It doesn’t require an integral vertex id and maintains the integrity of the expected type. -A problem with this is that if it fails, the error reported will be something like “doesn’t meet concept requirements” which is -obscure and takes time by the user to understand and resolve. - -\begin{lstlisting} - target_id(g,uv); -\end{lstlisting} -The final option allows the compiler to report a regular error or warning if the returned value isn't what's expected in the -context it's used because the types are included in the error message, making it easier to understand what the problem is. -Additionally, functions aren't distinguished by their return type, so there's little value in attempting to check it in this case. +The previous version of this interface defined separate concepts for targeted edges (with only \tcode{target_id}) and sourced edges +(with both \tcode{source_id} and \tcode{target_id}). The descriptor design makes this distinction +unnecessary: every edge descriptor carries enough information to provide both ids, so the single \tcode{edge} concept is sufficient. -There is precedent for this design choice of not validating the return type, as can be seen in the -\href{https://en.cppreference.com/w/cpp/ranges/sized_range}{\tcode{sized_range}}concept. +Return types are not validated in order to provide flexibility. Constraining the return type of \tcode{target_id(g,uv)} to \tcode{integral} +would exclude graphs whose vertex id is a non-integral key (e.g.\ a \tcode{string} for a \tcode{map}-based graph). Constraining to +\tcode{vertex_id_t} gives obscure error messages on failure. Leaving the return type unconstrained allows the compiler to +report the actual type mismatch at the point of use, which is easier to diagnose. There is precedent for this choice in the +\href{https://en.cppreference.com/w/cpp/ranges/sized_range}{\tcode{sized_range}} concept. \paragraph{Edge Range Concepts} -There is a single edge range concept. +\tcode{out_edge_range} constrains the range returned by \tcode{out_edges(g,u)}, and \tcode{in_edge_range} constrains +the range returned by \tcode{in_edges(g,u)} for bidirectional graphs. Both require a \tcode{forward_range} whose value type +satisfies \tcode{edge>}. {\small \lstinputlisting{D3130_Container_Interface/src/concepts_target_edge_range.hpp} } \subsubsection{Vertex Concepts} -The \tcode{vertex_range} concept is the general definition used for adjacency lists while \tcode{index_vertex_range} is used for -high performance graphs where vertices are typically stored in a \tcode{vector}. +The \tcode{vertex} concept requires that a vertex supports \tcode{vertex_id(g,u)} and \tcode{find_vertex(g,uid)}. +Building on it, \tcode{vertex_range} requires a \tcode{forward_range} and \tcode{sized_range} whose +value type satisfies \tcode{vertex}. + +Two specializations of vertex range are provided: +\begin{itemize} + \item \tcode{index_vertex_range} — vertex id is integral and the vertex range is random-access with an integral \tcode{storage_type}. Used for high-performance graphs such as \tcode{index_adjacency_list}. + \item \tcode{mapped_vertex_range} — vertex id is non-integral and vertices are found via \tcode{find_vertex(g, uid)}. Used for graphs whose vertices are stored in associative containers. +\end{itemize} {\small \lstinputlisting{D3130_Container_Interface/src/concepts_vertex_range.hpp} } \subsubsection{Adjacency List Concepts} -The adjacency list concepts bring together the vertex and edge concepts used for core graph concepts. -All algorithms initially proposed for the Graph Library use the \tcode{index_adjacency_list}. Future -proposals may use the \tcode{adjacency_list} concept which allows for vertices in associative containers. +The adjacency list concepts combine the vertex and edge concepts. Six concepts are defined, covering three axes: +\begin{itemize} + \item \tcode{adjacency_list} — base concept: \tcode{vertices(g)} returns a \tcode{vertex_range} and \tcode{out_edges(g,u)} returns an \tcode{out_edge_range}. + \item \tcode{index_adjacency_list} — \tcode{adjacency_list} with \tcode{index_vertex_range}. All algorithms currently proposed for the Graph Library use this concept. + \item \tcode{bidirectional_adjacency_list} — \tcode{adjacency_list} that also exposes \tcode{in_edges(g,u)} as an \tcode{in_edge_range} with accessible \tcode{source_id}. + \item \tcode{index_bidirectional_adjacency_list} — \tcode{bidirectional_adjacency_list} with \tcode{index_vertex_range}. + \item \tcode{mapped_adjacency_list} — \tcode{adjacency_list} with \tcode{mapped_vertex_range}. + \item \tcode{mapped_bidirectional_adjacency_list} — \tcode{bidirectional_adjacency_list} with \tcode{mapped_vertex_range}. +\end{itemize} + +The previous interface defined \tcode{basic_*} and \tcode{sourced_*} variants to distinguish edges with or without a source id. +The descriptor design makes this distinction unnecessary: all edge descriptors carry source information, so the \tcode{basic_*} +and \tcode{sourced_*} concept families have been removed. {\small \lstinputlisting{D3130_Container_Interface/src/concepts_adj_list.hpp} } -\phil{Verify we're using \tcode{adjacency_list} in the View and algorithm definitions.} - \subsection{Traits} Table \ref{tab:graph_traits} summarizes the type traits in the Graph Container Interface, allowing views and algorithms to query the graph's characteristics. @@ -110,17 +124,19 @@ \subsection{Traits} \hline \textbf{Trait} & \textbf{Type} & \textbf{Comment} \\ \hline - \tcode{has_degree} & concept & Is the \tcode{degree(g,u)} function available? \\ - \tcode{has_find_vertex} & concept & Are the \tcode{find_vertex(g,_)} functions available? \\ - \tcode{has_find_vertex_edge} & concept & Are the \tcode{find_vertex_edge(g,_)} functions available?\\ - \tcode{has_contains_edge} & concept & Is the \tcode{contains_edge(g,uid,vid)} function available?\\ -\hline - \tcode{define_unordered_edge : false_type} & struct & Specialize to derive from \tcode{true_type} for a graph with unordered edges \\ - \tcode{unordered_edge} & concept & \\ -\hline - \tcode{ordered_edge} & concept & \\ + \tcode{has_degree} & concept & Is the \tcode{out_degree(g,u)} function available? \\ + \tcode{has_find_vertex} & concept & Are the \tcode{find_vertex(g,\_)} functions available? \\ + \tcode{has_find_vertex_edge} & concept & Are the \tcode{find_out_edge(g,\_)} functions available?\\ + \tcode{has_contains_edge} & concept & Is the \tcode{contains_out_edge(g,uid,vid)} function available?\\ +\hdashline + \tcode{has_in_degree} & concept & Is the \tcode{in_degree(g,u)} function available? \\ + \tcode{has_find_in_edge} & concept & Are the \tcode{find_in_edge(g,\_)} functions available? \\ + \tcode{has_contains_in_edge} & concept & Is the \tcode{contains_in_edge(g,uid,vid)} function available?\\ +\hdashline + \tcode{has_basic_queries} & concept & \tcode{has_degree \&\& has_find_vertex \&\& has_find_vertex_edge} \\ + \tcode{has_full_queries} & concept & \tcode{has_basic_queries \&\& has_contains_edge>} \\ \hline - \tcode{define_adjacency_matrix : false_type} & struct & Specialize for graph implementation to derive from \tcode{true_type} for edges stored as a square 2-dimensional array \\ + \tcode{define_adjacency_matrix : false_type} & struct & Specialize for graph implementation to derive from \tcode{true_type} for edges stored as a square 2-dimensional array. Not yet in reference implementation. \\ \tcode{is_adjacency_matrix} & struct & \\ \tcode{is_adjacency_matrix_v} & type alias & \\ \tcode{adjacency_matrix} & concept & \\ @@ -144,25 +160,31 @@ \subsection{Types} \begin{table}[h!] \begin{center} %\resizebox{\textwidth}{!} -{\begin{tabular}{l l L{1.5cm}} +{\begin{tabular}{l l L{2.5cm}} \hline \textbf{Type Alias} & \textbf{Definition} & \textbf{Comment} \\ \hline - \tcode{graph_reference_t} & \tcode{add_lvalue_reference} & \\ \tcode{graph_value_t} & \tcode{decltype(graph_value(g))} & optional \\ \hline \tcode{vertex_range_t} & \tcode{decltype(vertices(g))} & \\ \tcode{vertex_iterator_t} & \tcode{iterator_t>} & \\ - \tcode{vertex_t} & \tcode{range_value_t>} & \\ - \tcode{vertex_reference_t} & \tcode{range_reference_t>} & \\ - \tcode{vertex_id_t} & \tcode{decltype(vertex_id(g,u))} & \\ + \tcode{vertex_t} & \tcode{range_value_t>} & descriptor \\ + \tcode{vertex_id_t} & \tcode{remove_cvref_t))>} & \\ + \tcode{vertex_id_store_t} & (see below) & normative \\ + \textit{\tcode{raw-vertex-id-type}} & \tcode{decltype(vertex_id(g,vertex_t))} & exposition only \\ \tcode{vertex_value_t} & \tcode{decltype(vertex_value(g,u))} & optional \\ \hline - \tcode{vertex_edge_range_t} & \tcode{decltype(edges(g,u))} & \\ - \tcode{vertex_edge_iterator_t} & \tcode{iterator_t>} & \\ - \tcode{edge_t} & \tcode{range_value_t>} & \\ - \tcode{edge_reference_t} & \tcode{range_reference_t>} & \\ + \tcode{out_edge_range_t} & \tcode{decltype(out_edges(g,vertex_t))} & \\ + \tcode{out_edge_iterator_t} & \tcode{iterator_t>} & \\ + \tcode{out_edge_t} & \tcode{range_value_t>} & descriptor \\ + \tcode{vertex_edge_range_t} & \tcode{out_edge_range_t} & alias \\ + \tcode{vertex_edge_iterator_t} & \tcode{out_edge_iterator_t} & alias \\ + \tcode{edge_t} & \tcode{out_edge_t} & alias \\ \tcode{edge_value_t} & \tcode{decltype(edge_value(g,uv))} & optional \\ +\hdashline + \tcode{in_edge_range_t} & \tcode{decltype(in_edges(g,vertex_t))} & \\ + \tcode{in_edge_iterator_t} & \tcode{iterator_t>} & \\ + \tcode{in_edge_t} & \tcode{range_value_t>} & descriptor \\ \hline \tcode{partition_id_t} & \tcode{decltype(partition_id(g,u))} & optional \\ \tcode{partition_vertex_range_t} & \tcode{vertices(g,pid)} & optional \\ @@ -185,6 +207,146 @@ \subsection{Classes and Structs} \emph{While we believe the use of concepts is appropriate for graphs as a range-of-ranges, we are marking them as "For exposition only" until we have consensus of whether they belong in the standard or not.} +\subsubsection{Data Structs} + +\tcode{vertex_data}, \tcode{edge_data} and \tcode{neighbor_data} are aggregates used by +views for structured-binding iteration. They are defined with multiple specializations +that void-out unused members so that the structured binding is compact. + +\tcode{vertex_data} has the following members depending on the template +parameters (a \tcode{void} parameter omits the corresponding member): + +\begin{table}[h!] +\begin{center} +{\begin{tabular}{l l L{7.5cm}} +\hline + \textbf{Members present} & \textbf{Specialization} & \textbf{Typical use} \\ +\hline + \tcode{id, vertex, value} & \tcode{} & Full vertexlist with descriptor and value \\ + \tcode{id, vertex} & \tcode{} & Vertexlist with descriptor, no value \\ + \tcode{id, value} & \tcode{} & Vertexlist with value, no descriptor \\ + \tcode{id} & \tcode{} & ID-only vertexlist \\ + \tcode{vertex, value} & \tcode{} & Descriptor-based, no id \\ + \tcode{vertex} & \tcode{} & Descriptor only \\ + \tcode{value} & \tcode{} & Value only \\ + (empty) & \tcode{} & No members \\ +\hline +\hline +\end{tabular}} +\caption{\tcode{vertex\_data} Specializations} +\label{tab:vertex_data} +\end{center} +\end{table} + +\tcode{edge_data} has the following members. The \tcode{Sourced} +boolean controls whether \tcode{source_id} is included. A \tcode{void} VId or EV omits +the corresponding member; a \tcode{void} E omits the edge descriptor member. + +\begin{table}[h!] +\begin{center} +{\begin{tabular}{l l L{6.5cm}} +\hline + \textbf{Members present} & \textbf{Specialization} & \textbf{Typical use} \\ +\hline + \tcode{source\_id, target\_id, edge, value} & \tcode{} & Sourced incidence with descriptor and value \\ + \tcode{source\_id, target\_id, edge} & \tcode{} & Sourced incidence with descriptor \\ + \tcode{source\_id, target\_id, value} & \tcode{} & Sourced incidence with value \\ + \tcode{source\_id, target\_id} & \tcode{} & Sourced incidence, IDs only \\ + \tcode{target\_id, edge, value} & \tcode{} & Unsourced incidence with descriptor and value \\ + \tcode{target\_id, edge} & \tcode{} & Unsourced incidence with descriptor \\ + \tcode{target\_id, value} & \tcode{} & Unsourced incidence with value \\ + \tcode{target\_id} & \tcode{} & Target ID only \\ +\hline +\hline +\end{tabular}} +\caption{\tcode{edge\_data} Specializations (VId present)} +\label{tab:edge_data} +\end{center} +\end{table} + +\tcode{neighbor_data} is analogous to \tcode{edge_data} but replaces +the edge descriptor with a target vertex descriptor. It is used by adjacency (neighbor) +views. + +The following helper type aliases simplify the most common structured-binding patterns: + +\begin{table}[h!] +\begin{center} +{\begin{tabular}{l L{8cm}} +\hline + \textbf{Alias} & \textbf{Definition} \\ +\hline + \tcode{copyable_vertex_t} & \tcode{vertex_data} --- \texttt{\{id, value\}} \\ + \tcode{copyable_edge_t} & \tcode{edge_data} --- \texttt{\{source\_id, target\_id, value\}} \\ + \tcode{copyable_neighbor_t} & \tcode{neighbor_data} --- \texttt{\{source\_id, target\_id, value\}} \\ +\hline +\hline +\end{tabular}} +\caption{Helper type aliases for common structured-binding patterns} +\label{tab:copyable_types} +\end{center} +\end{table} + +\subsection{Value Function Concepts} + +\tcode{vertex_value_function} and \tcode{edge_value_function} define the required +interface for callables passed to views and algorithms to extract per-vertex or per-edge +scalar values (e.g.\ a weight or a label). Both concepts use the two-argument +\tcode{f(const G\&, descriptor)} convention so that stateless lambdas may be captured +into \tcode{std::views} pipelines without holding a reference to the graph. + +\begin{lstlisting} +template +concept vertex_value_function = + invocable && + (!is_void_v>); + +template +concept edge_value_function = + invocable && + (!is_void_v>); +\end{lstlisting} + +\tcode{basic_edge_weight_function} (defined in the algorithms proposal) refines +\tcode{edge_value_function} with the additional requirement that the return type is +arithmetic. + +\subsection{Vertex Property Map} + +Algorithms that maintain per-vertex state (visited flags, distances, component labels, +etc.) need a container indexed by vertex ID. For index-based graphs a \tcode{vector} is +natural; for mapped (key-based) graphs an \tcode{unordered_map} is required. +\tcode{vertex_property_map} abstracts over this difference. + +\begin{lstlisting} +template +using vertex_property_map = + conditional_t, + vector, + unordered_map, T>>; +\end{lstlisting} + +Four helper functions complete the interface: + +\begin{table}[h!] +\begin{center} +\resizebox{\textwidth}{!} +{\begin{tabular}{l L{9cm}} +\hline + \textbf{Function} & \textbf{Description} \\ +\hline + \tcode{make_vertex_property_map(g, init)} & Eager factory: creates a map with every vertex pre-populated to \tcode{init}. O(V). \\ + \tcode{make_vertex_property_map(g)} & Lazy factory: creates an empty map with capacity reserved. O(V) for index, O(1) for mapped. \\ + \tcode{vertex_property_map_contains(m, uid)} & Returns \tcode{true} if \tcode{uid} has an entry (\tcode{uid < size(m)} for \tcode{vector}; \tcode{m.contains(uid)} for \tcode{unordered\_map}). \\ + \tcode{vertex_property_map_get(m, uid, dflt)} & Returns the stored value or \tcode{dflt} if absent. Does not insert. \\ +\hline +\hline +\end{tabular}} +\caption{Vertex Property Map Functions} +\label{tab:vpm_func} +\end{center} +\end{table} + \subsection{Functions} Tables \ref{tab:graph_func}, \ref{tab:vertex_func} and \ref{tab:edge_func} summarize the primitive functions in the Graph Container Interface. @@ -202,8 +364,8 @@ \subsection{Functions} \tcode{graph_value(g)} & \tcode{graph_value_t} & constant & n/a, optional \\ \tcode{vertices(g)} & \tcode{vertex_range_t} & constant & \tcode{g} if \tcode{random_access_range}, n/a otherwise \\ \tcode{num_vertices(g)} & \tcode{integral} & constant & \tcode{size(vertices(g))} \\ - \tcode{num_edges(g)} & \tcode{integral} & |E| & \tcode{n=0; for(u: vertices(g)) n+=distance(edges(g,u)); return n;} \\ - \tcode{has_edge(g)} & \tcode{bool} & |V| & \tcode{for(u: vertices(g)) if !empty(edges(g,u)) return true; return false;} \\ + \tcode{num_edges(g)} & \tcode{integral} & |E| & \tcode{n=0; for(u: vertices(g)) n+=distance(out\_edges(g,u)); return n;} \\ + \tcode{has_edges(g)} & \tcode{bool} & |V| & \tcode{for(u: vertices(g)) if !empty(out\_edges(g,u)) return true; return false;} \\ \hdashline \tcode{num_partitions(g)} & \tcode{integral} & constant & 1 \\ \tcode{vertices(g,pid)} & \tcode{partition_vertex_range_t} & constant & \tcode{vertices(g)} \\ @@ -214,7 +376,7 @@ \subsection{Functions} \label{tab:graph_func} \end{center} \end{table} -The complexity shown above for \tcode{num_edges(g)} and \tcode{has_edge(g)} is for the default implementation. +The complexity shown above for \tcode{num_edges(g)} and \tcode{has_edges(g)} is for the default implementation. Specific graph implementations may have better characteristics. The only vertex function that requires a vertex id \tcode{(uid)} is \tcode{find_vertex(g,uid)}. The other @@ -239,10 +401,16 @@ \subsection{Functions} \tcode{vertex_value(g,u)} & \tcode{vertex_value_t} & constant & n/a, optional \\ \tcode{vertex_value(g,uid)} & \tcode{vertex_value_t} & constant & \tcode{vertex_value(g,*find_vertex(g,uid))}, \\ & & & optional \\ - \tcode{degree(g,u)} & \tcode{integral} & constant & \tcode{size(edges(g,u))} if \tcode{sized_range>} \\ - \tcode{degree(g,uid)} & \tcode{integral} & constant & \tcode{size(edges(g,uid))} if \tcode{sized_range>} \\ - \tcode{edges(g,u)} & \tcode{vertex_edge_range_t} & constant & \tcode{u} if \tcode{forward_range>}, n/a otherwise \\ - \tcode{edges(g,uid)} & \tcode{vertex_edge_range_t} & constant & \tcode{edges(g,*find_vertex(g,uid))} \\ + \tcode{out_edges(g,u)} & \tcode{out_edge_range_t} & constant & \tcode{u} if \tcode{forward_range>}, n/a otherwise \\ + \tcode{out_edges(g,uid)} & \tcode{out_edge_range_t} & constant & \tcode{out\_edges(g,*find\_vertex(g,uid))} \\ + \tcode{out_degree(g,u)} & \tcode{integral} & constant & \tcode{size(out\_edges(g,u))} if \tcode{sized_range>} \\ + \tcode{out_degree(g,uid)} & \tcode{integral} & constant & \tcode{size(out\_edges(g,uid))} if \tcode{sized_range>} \\ + (\tcode{edges}, \tcode{degree} are backward-compatible aliases for \tcode{out\_edges}, \tcode{out\_degree}) & & & \\ +\hdashline + \tcode{in_edges(g,u)} & \tcode{in_edge_range_t} & constant & n/a (requires bidirectional graph) \\ + \tcode{in_edges(g,uid)} & \tcode{in_edge_range_t} & constant & \tcode{in\_edges(g,*find\_vertex(g,uid))} \\ + \tcode{in_degree(g,u)} & \tcode{integral} & constant & \tcode{size(in\_edges(g,u))} if \tcode{sized_range>} \\ + \tcode{in_degree(g,uid)} & \tcode{integral} & constant & \tcode{size(in\_edges(g,uid))} if \tcode{sized_range>} \\ \hdashline \tcode{partition_id(g,u)} & \tcode{partition_id_t} & constant & \\ \tcode{partition_id(g,uid)} & \tcode{partition_id_t} & constant & \\ @@ -254,8 +422,8 @@ \subsection{Functions} \end{center} \end{table} -The default implementation for the \tcode{degree} functions assumes that \tcode{vertex_edge_range_t} is a sized range -to have constant complexity. If the underlying container has a non-linear \tcode{size(R)} function, the \tcode{degree} functions will +The default implementation for the \tcode{out_degree} and \tcode{in_degree} functions assumes that \tcode{out_edge_range_t} or \tcode{in_edge_range_t} is a sized range +to have constant complexity. If the underlying container has a non-linear \tcode{size(R)} function, the degree functions will also be non-linear. This is expected to be an uncommon case. \begin{table}[h!] @@ -271,22 +439,28 @@ \subsection{Functions} & & & \hspace{3mm}\tcode{\&\& integral} \\ \tcode{edge_value(g,uv)} & \tcode{edge_value_t} & constant & \tcode{uv} if \tcode{forward_range>}, \\ & & & n/a otherwise, optional \\ - \tcode{find_vertex_edge(g,u,vid)} & \tcode{vertex_edge_t} & linear & \tcode{find(edges(g,u),} \\ - & & & \tcode{[](uv) \{ return target_id(g,uv)==vid; \})} \\ - \tcode{find_vertex_edge(g,uid,vid)} & \tcode{vertex_edge_t} & linear & \tcode{find_vertex_edge(} \\ - & & & \hspace{8mm}\tcode{g, *find_vertex(g,uid), vid)} \\ - \tcode{contains_edge(g,uid,vid)} & \tcode{bool} & constant & \tcode{uid < size(vertices(g))} \\ - & & & \tcode{\&\& vid < size(vertices(g))} \\ - & & & \hspace{3mm}if \tcode{is_adjacency_matrix_v}.\\ - & & linear & \tcode{find_vertex_edge(g,uid,vid)} \\ - & & & \tcode{!= end(edges(g,uid))} otherwise. \\ -\hdashline - \multicolumn{4}{c}{The following are only available when the optional \tcode{source_id(g,uv)} is defined for the edge} \\ + \tcode{find_out_edge(g,u,vid)} & \tcode{out_edge_t} & linear & \tcode{find(out\_edges(g,u),} \\ + & & & \tcode{[](uv) \{ return target\_id(g,uv)==vid; \})} \\ + \tcode{find_out_edge(g,uid,vid)} & \tcode{out_edge_t} & linear & \tcode{find\_out\_edge(} \\ + & & & \hspace{8mm}\tcode{g, *find\_vertex(g,uid), vid)} \\ + (\tcode{find\_vertex\_edge} is a backward-compatible alias for \tcode{find\_out\_edge}) & & & \\ + \tcode{contains_out_edge(g,uid,vid)} & \tcode{bool} & constant & \tcode{uid < size(vertices(g))} \\ + & & & \tcode{\&\& vid < size(vertices(g))} \\ + & & & \hspace{3mm}if \tcode{is_adjacency_matrix_v}.\\ + & & linear & \tcode{find\_out\_edge(g,uid,vid)} \\ + & & & \tcode{!= end(out\_edges(g,uid))} otherwise. \\ + (\tcode{contains\_edge} is a backward-compatible alias for \tcode{contains\_out\_edge}) & & & \\ \hdashline - \tcode{source_id(g,uv)} & \tcode{vertex_id_t} & constant & n/a, optional \\ - \tcode{source(g,uv)} & \tcode{vertex_t} & constant & \tcode{*(begin(vertices(g)) + source_id(g,uv))} \\ + \tcode{source_id(g,uv)} & \tcode{vertex_id_t} & constant & mandatory for descriptor-based edges \\ + \tcode{source(g,uv)} & \tcode{vertex_t} & constant & \tcode{*(begin(vertices(g)) + source\_id(g,uv))} \\ & & & if \tcode{random_access_range>} \\ - & & & \hspace{3mm}\tcode{\&\& integral} \\ + & & & \hspace{3mm}\tcode{\&\& integral} \\ +\hdashline + \tcode{find_in_edge(g,u,vid)} & \tcode{in_edge_t} & linear & n/a (requires bidirectional graph) \\ + \tcode{find_in_edge(g,uid,vid)} & \tcode{in_edge_t} & linear & \tcode{find\_in\_edge(} \\ + & & & \hspace{8mm}\tcode{g, *find\_vertex(g,uid), vid)} \\ + \tcode{contains_in_edge(g,uid,vid)} & \tcode{bool} & linear & \tcode{find\_in\_edge(g,uid,vid)} \\ + & & & \tcode{!= end(in\_edges(g,uid))} \\ \hline \end{tabular}} \caption{Edge Functions} @@ -332,47 +506,85 @@ \subsection{Determining the vertex\_id and its type} \end{enumerate} \subsection{Vertex and Edge Descriptor Views} -The ranges returned by \tcode{vertices(g)} and \tcode{edges(g,u)} are views of their respective underlying -container in the adjacency list. The value\_type of the view is a descriptor, which refers to an object in -the underlying range. - -Descriptors are opaque, abstract objects that represent vertices and edges in an adjacency list. They are -particularly useful because they abstract away the implementation details of the adjacency list, allowing -a user to work with different graph types in a consistent manner. - -A benefit of using descriptors is that it reduces the number of functions and concepts needed for the GCI -compared to previous designs. Without them, an interface would require additional functions and concepts, -and algorithms would need to be specialized for vertices stored in random-access containers compared to -associative containers. - -Practically, a descriptor is either an integral index or an iterator, depending on the underlying container of vertices -or edges. For example, a descriptor for a vertex in a \tcode{vector} is an index, while the descriptor for an edge in a -\tcode{list} is an iterator. Looking forward to the future, beyond this proposal, a descriptor for a vertex in a -\tcode{map} or \tcode{unordered\_map} would use an iterator; the door is opened for future expansion with minimal -impact to the GCI. - -The vertex and edge descriptors are defined as the \tcode{vertex_t} and \tcode{edge_t} types, respectively. -The following are characteristics of descriptors: -\begin{itemize} - \item Equality comparison: Descriptors can be compared for equality using the \tcode{==} and \tcode{!=} operators. - \item Ordering: Descriptors can be ordered using the \tcode{<}, \tcode{<=}, \tcode{>}, and \tcode{>=} operators, if supported - by the iterators in underlying container being used. - \item Copy and assignment: Descriptors can be copied and assigned, ensuring they can be used in standard algorithms and containers. - \item Default construction: Descriptors can be default-constructed, though the resulting values are not guaranteed to represent - a valid vertex or edge. -\end{itemize} +Graph containers may store vertices and edges in very different data structures: a +\tcode{vector}-based adjacency list uses integer indices as vertex identifiers while a +\tcode{map}-based graph uses the map key. Exposing raw iterators directly would require +concepts, algorithms, and user code to handle each storage strategy separately. +Descriptors solve this by providing a lightweight, opaque handle that works uniformly +regardless of the underlying container type. Table~\ref{tab:desc_vs_iter} compares raw +iterators with descriptors. + +\begin{table}[h!] +\begin{center} +\resizebox{\textwidth}{!} +{\begin{tabular}{l L{5.5cm} L{7.0cm}} +\hline + \textbf{Concern} & \textbf{Raw iterators} & \textbf{Descriptors} \\ +\hline + Abstraction & Expose container layout & Hide storage strategy --- one concept covers indexed and keyed graphs \\ + Overload reduction & Separate overloads for index vs.\ iterator & A single overload accepts the descriptor \\ + Invalidation & Invalidated by container mutation & Can be re-resolved via \tcode{find_vertex} \\ + Copyability & Varies (e.g.\ \tcode{forward_list::iterator}) & Always trivially copyable or cheap \\ + Size & May be pointer-sized or larger & Index-based: one integer \\ +\hline +\hline +\end{tabular}} +\caption{Raw iterators vs.\ descriptors} +\label{tab:desc_vs_iter} +\end{center} +\end{table} + +Index-based vertex descriptors provide an additional level of abstraction: they can +refer to a vertex even when no physical vertex object exists in the graph. +A \tcode{compressed_graph}, for example, stores only edges; there is no C++ object +corresponding to each vertex. Because the vertex is identified purely by its index, the +GCI's algorithms and views work identically whether or not the container materialises +explicit vertex storage. An iterator-based approach cannot offer this property --- an +iterator must point to an actual object. + +CPOs automatically detect the storage pattern of an adjacency list and select the +appropriate descriptor strategy: \textbf{random-access} containers (\tcode{vector}, +\tcode{deque}) produce index-based vertex descriptors; \textbf{associative} containers +(\tcode{map}, \tcode{unordered_map}) produce key-based (iterator) descriptors. Edge +patterns --- \tcode{int}, \tcode{pair}, \tcode{tuple}, or custom structs +--- are recognised automatically so that \tcode{target_id}, \tcode{source_id}, and +\tcode{edge_value} CPOs are provided without user specialisation. + +The vertex and edge descriptors are defined as the \tcode{vertex_t} and +\tcode{edge_t} types, respectively. \tcode{vertex_descriptor} stores +either a \tcode{size_t} index (for random-access containers) or an iterator (for +associative containers) as an exposition-only private member. +\tcode{edge_descriptor} additionally stores the +source \tcode{vertex_descriptor} and an \tcode{EdgeDirection} tag (\tcode{out_edge_tag} +or \tcode{in_edge_tag}) that controls source/target semantics, allowing bidirectional +graphs to use the same edge type for both outgoing and incoming edges. + +Both descriptor types support equality comparison (\tcode{==}, \tcode{!=}), ordering +(\tcode{<}, \tcode{<=}, \tcode{>}, \tcode{>=} where supported), copy, assignment, and +default construction. They also provide \tcode{underlying_value(c)} and +\tcode{inner_value(c)} member function templates, where \tcode{c} is the owning +container, to retrieve a reference to the underlying element. These are needed only +when overriding CPOs to adapt an external graph container to the GCI. + +Because descriptors are small and trivially copyable, CPO signatures accept them by +value. A \tcode{vertex_descriptor} is at most pointer-sized (one index or iterator). +An \tcode{edge_descriptor} is larger because it also carries the source +\tcode{vertex_descriptor}, but is still cheap to copy. The \tcode{const\&} form appears +only in \tcode{requires} clauses of concepts where value copies are not permitted. +{\small + \lstinputlisting{D3130_Container_Interface/src/descriptor.hpp} +} + +The following type traits identify whether a type is a vertex or edge descriptor. +{\small + \lstinputlisting{D3130_Container_Interface/src/descriptor_traits.hpp} +} -In addition, a descriptor has an \tcode{inner_value()} member function that returns a reference to the underlying value -in the underlying container. This is only needed for overriding the customization points when you're adapting your own -graph container to the GCI. - The only vertex function that requires a vertex id \tcode{(uid)} is \tcode{find_vertex(g,uid).} All other functions that accept vertex id are convenience functions that imply a call to \tcode{find_vertex(g,uid)} to get the vertex descriptor before a call. -The following are the descriptor views used by \tcode{vertices(g)} and \tcode{edges(g,u)}. \tcode{descriptor_subrange_view} -is used when edges for multiple vertices are stored in a single container, such as a CSR or adjacency matrix -(e.g. \tcode{compressed\_graph}). +The following are the descriptor views used by \tcode{vertices(g)} and \tcode{out_edges(g,u)}. {\small \lstinputlisting{D3130_Container_Interface/src/descriptor_view.hpp} } @@ -400,7 +612,7 @@ \subsection{Unipartite, Bipartite and Multipartite Graph Representation} then return the appropriate value. \end{itemize} -\tcode{edges(g,uid,pid)} and \tcode{edges(g,u,pid)} filter the edges where the target is in the partition \tcode{pid} passed. +\tcode{out_edges(g,uid,pid)} and \tcode{out_edges(g,u,pid)} filter the edges where the target is in the partition \tcode{pid} passed. This isn't needed for bipartite graphs. \section{Edgelist Interface} @@ -408,19 +620,32 @@ \section{Edgelist Interface} adjacency list or edges in the incidence view, but is a distinct range of values that are separate from the others. Like the adjacency list, the edgelist has default implementations that use the standard library for simple implementations out of the box. -It's also able to easily adapt to externally defined edge types by overriding the \tcode{source_id(e)}, \tcode{target_id(e)} and -\tcode{edge_value(e)} functions. +It's also able to easily adapt to externally defined edge types by overriding the \tcode{source_id(el,uv)}, \tcode{target_id(el,uv)} and +\tcode{edge_value(el,uv)} functions. \subsection{Namespace} -The concepts and types for the edgelist are defined in the \tcode{std::graph::edgelist} namespace to avoid conflicts with the adjacency list. +Edge list concepts, type aliases and CPOs are defined in the \tcode{std::graph::edge\_list} namespace. +Unlike the adjacency list, these symbols are \emph{not} re-exported into \tcode{std::graph} because +certain names have distinct meanings in each context --- most notably \tcode{vertex\_id\_t} is derived +from \tcode{vertex\_id(g,u)} for an adjacency list but from \tcode{target\_id(el,uv)} for an edge list. +Bringing both into the root namespace would create an irresolvable ambiguity. -\phil{It would be nice to include them in std::graph, but I'm having difficulty figuring out how to do that based on adjlist and edgelist concepts.} +\tcode{graph::adj\_list} and \tcode{graph::edge\_list} are \emph{peer} namespaces: neither is a +subset or refinement of the other. An adjacency list provides per-vertex edge ranges and supports fast +neighbour traversal; an edge list is a flat range of \tcode{(source, target [, value])} tuples suited to +edge-centric algorithms such as Kruskal's MST or bulk construction. CPOs that operate directly on +edge data --- \tcode{source\_id}, \tcode{target\_id} and \tcode{edge\_value} --- are shared and +live in \tcode{std::graph}. \subsection{Concepts} -The concepts for edgelists follow the same naming conventions as the adjacency lists. -% The "basic" qualifier has been dropped WRT integral indexes. There's no reason to have a distinction between the two. +The edgelist concepts use a 2-argument form \tcode{f(el, uv)} where \tcode{el} is the edgelist and \tcode{uv} is the edge value, +matching the adjacency list CPO convention. All edgelist CPOs require both the container and the element to allow for +container-specific dispatch. +\tcode{basic_sourced_edgelist} is the base concept: an input range whose element type is not itself a range, supporting +\tcode{source_id(el,uv)} and \tcode{target_id(el,uv)} with compatible types. \tcode{basic_sourced_index_edgelist} additionally +requires both ids to be integral. \tcode{has_edge_value} refines \tcode{basic_sourced_edgelist} to also provide \tcode{edge_value(el,uv)}. {\small \lstinputlisting{D3130_Container_Interface/src/edgelist_concepts.hpp} } @@ -453,7 +678,7 @@ \subsection{Types} The type aliases are defined by either a function specialization for the edgelist implementation, or a refinement of one of those types (e.g. an iterator of a range). Table \ref{tab:edgelist_func} describes the functions in more detail. -\tcode{edge_value(g,uv)} can be optionally implemented, depending on whether or not the edgelist has values on the edge types. +\tcode{edge_value(el,uv)} can be optionally implemented, depending on whether or not the edgelist has values on the edge types. \begin{table}[h!] \begin{center} @@ -465,10 +690,10 @@ \subsection{Types} \tcode{edge_range_t} & \tcode{EL} & \\ \tcode{edge_iterator_t} & \tcode{iterator_t>} & \\ \tcode{edge_t} & \tcode{range_value_t>} & \\ - \tcode{edge_reference_t} & \tcode{range_reference_t>} & \\ - \tcode{edge_value_t} & \tcode{decltype(edge_value(e))} & optional \\ + \tcode{edge_value_t} & \tcode{decltype(edge_value(el,uv))} & optional \\ \hline - \tcode{vertex_id_t} & \tcode{decltype(target_id(e))} & \\ + \tcode{vertex_id_t} & \tcode{remove_cvref_t} & \\ + \textit{\tcode{raw-vertex-id-type}} & \tcode{decltype(target_id(el,uv))} & exposition only \\ \hline \end{tabular}} \caption{Edgelist Interface Type Aliases} @@ -480,7 +705,7 @@ \subsection{Types} % \lstinputlisting{D3130_Container_Interface/src/edgelist_types.hpp} %} \subsection{Functions} -Table \ref{tab:edgelist_func} shows the functions available in the Edgelist Interface. Unlike the adjacency list, \tcode{source_id(e)} +Table \ref{tab:edgelist_func} shows the functions available in the Edgelist Interface. Unlike the adjacency list, \tcode{source_id(el,uv)} is always available. @@ -491,15 +716,15 @@ \subsection{Functions} \hline \textbf{Function} & \textbf{Return Type} & \textbf{Cmplx} & \textbf{Default Implementation} \\ \hline - \tcode{target_id(e)} & \tcode{vertex_id_t} & constant & (see below) \\ - \tcode{source_id(e)} & \tcode{vertex_id_t} & constant & (see below) \\ - \tcode{edge_value(e)} & \tcode{edge_value_t} & constant & optional, see below \\ + \tcode{target_id(el,uv)} & \tcode{vertex_id_t} & constant & (see below) \\ + \tcode{source_id(el,uv)} & \tcode{vertex_id_t} & constant & (see below) \\ + \tcode{edge_value(el,uv)} & \tcode{edge_value_t} & constant & optional, see below \\ \hdashline - \tcode{contains_edge(el,uid,vid)} & \tcode{bool} & linear & \tcode{find_if(el, [](edge_reference_t e) \{} \\ - & & & \hspace{7mm}\tcode{return source_id(e)==uid} \\ - & & & \hspace{13.5mm}\tcode{\&\& target_id(e)==vid; \} )} \\ + \tcode{contains_edge(el,uid,vid)} & \tcode{bool} & linear & \tcode{find_if(el, [](auto\&\& uv) \{} \\ + & & & \hspace{7mm}\tcode{return source\_id(el,uv)==uid} \\ + & & & \hspace{13.5mm}\tcode{\&\& target\_id(el,uv)==vid; \} )} \\ \tcode{num_edges(el)} & \tcode{integral} & constant & \tcode{size(el)} \\ - \tcode{has_edge(el)} & \tcode{bool} & constant & \tcode{num_edges(el)>0} \\ + \tcode{has_edges(el)} & \tcode{bool} & constant & \tcode{num_edges(el)>0} \\ \hline \end{tabular}} \caption{Edgelist Interface Functions} @@ -511,19 +736,26 @@ \subsection{Functions} % [](edge_reference_t) \{return source_id(e)==uid && target_id(e)==vid\} \subsection{Determining the source\_id, target\_id and edge\_value types} -Special patterns are recognized for edges based on the \tcode{tuple} and \tcode{edge_info} types. When they are used the \tcode{source_id(e)}, -\tcode{target_id(e)} and \tcode{edge_value} functions will be defined automatically. +Special patterns are recognized for edges based on the \tcode{pair}, \tcode{tuple} and \tcode{edge_data} types. When they are used the \tcode{source_id(el,uv)}, +\tcode{target_id(el,uv)} and \tcode{edge_value(el,uv)} functions will be defined automatically. + +The \tcode{pair} pattern is +\begin{itemize} + \item \tcode{pair} for \tcode{source_id(el,uv)} and \tcode{target_id(el,uv)} respectively. +\end{itemize} The \tcode{tuple} patterns are \begin{itemize} - \item \tcode{tuple} for \tcode{source_id(e)} and \tcode{target_id(e)} respectively. - \item \tcode{tuple} for \tcode{source_id(e)}, \tcode{target_id(e)} and \tcode{edge_value(e)} respectively. + \item \tcode{tuple} for \tcode{source_id(el,uv)} and \tcode{target_id(el,uv)} respectively. + \item \tcode{tuple} for \tcode{source_id(el,uv)}, \tcode{target_id(el,uv)} and \tcode{edge_value(el,uv)} respectively. \end{itemize} -The \tcode{edge_info} patterns are +The \tcode{edge_data} patterns are \begin{itemize} - \item \tcode{edge_info} with \tcode{source_id(e)} and \tcode{target_id(e)}. - \item \tcode{edge_info} with \tcode{source_id(e)}, \tcode{target_id(e)} and \tcode{edge_value(e)}. + \item \tcode{edge_data} with \tcode{source_id(el,uv)} and \tcode{target_id(el,uv)}. + \item \tcode{edge_data} with \tcode{source_id(el,uv)}, \tcode{target_id(el,uv)} and \tcode{edge_value(el,uv)}. + \item \tcode{edge_data} with \tcode{source_id(el,uv)}, \tcode{target_id(el,uv)} and an edge descriptor reference. + \item \tcode{edge_data} with \tcode{source_id(el,uv)}, \tcode{target_id(el,uv)}, an edge descriptor reference and \tcode{edge_value(el,uv)}. \end{itemize} In all other cases the functions will need to be overridden for the edge type. diff --git a/D3130_Container_Interface/tex/revision.tex b/D3130_Container_Interface/tex/revision.tex index 2f9d647..78da8e0 100644 --- a/D3130_Container_Interface/tex/revision.tex +++ b/D3130_Container_Interface/tex/revision.tex @@ -70,3 +70,57 @@ \subsection*{\paperno r3} with \tcode{descriptor}, which is now copyable. \end{itemize} \end{itemize} + +\subsection*{\paperno r4} + +\begin{itemize} + \item \textbf{Associative-container support}: vertex and edge containers may now be + associative (e.g.\ \tcode{std::map}, \tcode{std::unordered_map}), enabling + graphs with non-integer or string vertex ids. Related changes: + \begin{itemize} + \item New concept \tcode{mapped_vertex_range} alongside \tcode{index_vertex_range}. + \item New type aliases \tcode{vertex_id_store_t} and \textit{raw-vertex-id-type}. + \item New \tcode{vertex_property_map} utility type alias; + \tcode{make_vertex_property_map} (eager and lazy), + \tcode{vertex_property_map_contains}, \tcode{vertex_property_map_get}. + \end{itemize} + \item \textbf{Bidirectional graph support}: a graph may now expose in-edge ranges + alongside out-edge ranges, enabling efficient reverse traversal. Related changes: + \begin{itemize} + \item New concept \tcode{in_edge_range} alongside \tcode{out_edge_range} + (together replacing \tcode{targeted_edge_range}). + \item Existing edge CPOs renamed with \tcode{out\_} prefix for symmetry: + \tcode{edges(g,u)} $\rightarrow$ \tcode{out_edges(g,u)} (\tcode{edges} kept as alias); + \tcode{degree(g,u)} $\rightarrow$ \tcode{out_degree(g,u)}; + \tcode{find_vertex_edge} $\rightarrow$ \tcode{find_out_edge}; + \tcode{contains_edge} $\rightarrow$ \tcode{contains_out_edge}. + \item New in-edge CPOs: \tcode{in_edges}, \tcode{in_degree}, + \tcode{find_in_edge}, \tcode{contains_in_edge}. + \item New type aliases \tcode{in_edge_range_t} and \tcode{in_edge_t}; + \tcode{vertex_edge_range_t} / \tcode{edge_t} retained as aliases for + \tcode{out_edge_range_t} / \tcode{out_edge_t}. + \end{itemize} + \item Ground the descriptor types introduced in r3 with concrete specifications backed by a working + prototype: \tcode{vertex_descriptor} and + \tcode{edge_descriptor} now have fully specified, + exposition-only internals. Descriptor traits (\tcode{is_vertex_descriptor_v}, + \tcode{is_edge_descriptor_v}, etc.) are defined in a dedicated header. + \tcode{vertex_descriptor_view} and \tcode{edge_descriptor_view} replace the former + \tcode{descriptor_view}. + \item Simplify the concept hierarchy: the single \tcode{edge} concept replaces the previous + \tcode{targeted_edge}/\tcode{sourced_edge} split; six adjacency list concepts cover the + index/mapped/bidirectional axes; \tcode{basic_*} and \tcode{sourced_*} variants are removed. + \item Removed type aliases: \tcode{graph_reference_t}, \tcode{vertex_reference_t}, + \tcode{edge_reference_t}. + \item New concepts: \tcode{vertex_value_function} and \tcode{edge_value_function} (value-function + concepts for views and algorithms). + \item Edgelist namespace renamed from \tcode{std::graph::edgelist} to \tcode{std::graph::edge_list}. + Adjacency list symbols now formally reside in \tcode{std::graph::adj_list} and are re-exported + to \tcode{std::graph}; the two sub-namespaces are peers. + \item \tcode{vertex_data}, \tcode{edge_data} and \tcode{neighbor_data} aggregates + (renamed from \tcode{vertex_info}, \tcode{edge_info}, \tcode{neighbor_info}) + with partial-specialisation families supply the value types held in associative + containers. + \item Edgelist CPOs moved to 2-argument form \tcode{f(el,uv)}; \tcode{edge_info} renamed to + \tcode{edge_data}; \tcode{edge_reference_t} removed; \tcode{raw-vertex-id-type} added. +\end{itemize} diff --git a/agents/gci_update_plan.md b/agents/gci_update_plan.md new file mode 100644 index 0000000..0c0f111 --- /dev/null +++ b/agents/gci_update_plan.md @@ -0,0 +1,517 @@ +# GCI Update — Implementation Plan + +## Governance + +| Item | Value | +|------|-------| +| Strategy | `agents/gci_update_strategy.md` | +| Branch | `gci_paper` (P1709 repo) | +| Authoritative source | graph-v3 implementation | +| Scope | D3130 paper + source files, cross-paper fixups, graph-v3 implementation changes | +| Verification | LaTeX compiles (`make D3130_Container_Interface`), graph-v3 builds and tests pass | + +### Commit discipline + +Each phase ends with a commit. Phases are ordered so every intermediate commit leaves +the LaTeX compilable (possibly with `\phil` TODO notes for forward references, but no +broken `\lstinputlisting` or undefined `\tcode` references). Implementation changes +have their own phase with a separate commit in the graph-v3 repo. + +### Verification gates + +| Gate | Command | When | +|------|---------|------| +| LaTeX builds | `make D3130_Container_Interface` in P1709 | After every paper phase | +| graph-v3 builds | `cmake --build build/linux-gcc-debug` in graph-v3 | After Phase I | +| graph-v3 tests | `ctest --test-dir build/linux-gcc-debug` in graph-v3 | After Phase I | + +Implementation gate precondition (graph-v3): ensure `build/linux-gcc-debug` is already +configured, or run `cmake -S . -B build/linux-gcc-debug -DCMAKE_BUILD_TYPE=Debug` first. + +--- + +## Phase overview + +| Phase | Name | Repo | Depends on | Risk | Status | +|-------|------|------|------------|------|--------| +| A | Source file rewrite | P1709 | — | Low — self-contained `.hpp` files | ✅ Done (`c5fc672`) | +| B | Type alias & traits tables | P1709 | A | Low — table-only edits | ✅ Done (`81014ef`) | +| C | Function tables & CPOs | P1709 | B | Medium — cross-references with concepts | ✅ Done (`58ce4ab`) | +| D | Concept sections | P1709 | A | Medium — concepts reference source files | ✅ Done (`8de6a14`) | +| E | Descriptor section rewrite | P1709 | A | Medium — large prose rewrite | ✅ Done (`78680b3`) | +| F | Edgelist section | P1709 | A, B | Medium — namespace change + pattern rename | ✅ Done (`ca08466`) | +| G | New sections | P1709 | B, D | Low — additive | ✅ Done (`66e30d2`) | +| H | Prose audit & cleanup | P1709 | A–G | Low — mechanical search-and-replace | ✅ Done (`0cdcd7f`, `d095f87`) | +| I | Implementation changes | graph-v3 | — (independent) | Medium — must pass tests | ✅ Done (`ae4910c`) | +| J | Cross-paper fixups | P1709 | H | Low — mechanical | ✅ Done (`34f6068`) | + +Phases A–H are sequential within P1709 (each committed separately). +Phase I is independent and can be done before, after, or interleaved with the paper phases. +Phase J is last because it touches other papers and should only run once D3130 is stable. + +--- + +## Phase A — Source File Rewrite + +**Goal:** Replace all 11 `.hpp` files under `D3130_Container_Interface/src/` so that +`\lstinputlisting` directives in the LaTeX produce correct output immediately. + +### Files to delete + +| File | Reason | +|------|--------| +| `concepts_edges_before.hpp` | All `basic_*` edge concepts removed | +| `concepts_basic_adj_list.hpp` | All `basic_*` / `sourced_*` adj list concepts removed | + +### Files to rewrite in place + +| File | Old content | New content (modelled on graph-v3) | +|------|-------------|-------------------------------------| +| `concepts_edges.hpp` | 6 edge concepts (`targeted_edge`, `sourced_edge`, etc.) | Single `edge` concept — `requires(G& g, const E& e) { source_id(g, e); target_id(g, e); }` — no trait gate (Q3) | +| `concepts_target_edge_range.hpp` | `targeted_edge_range` + `basic_targeted_edge_range` | `out_edge_range` + `in_edge_range` — `forward_range && edge>` | +| `concepts_vertex_range.hpp` | `_common_vertex_range`, `vertex_range`, `index_vertex_range` | `vertex`, `vertex_range`, `index_vertex_range`, `mapped_vertex_range` | +| `concepts_adj_list.hpp` | `adjacency_list`, `index_adjacency_list`, `sourced_*` | `adjacency_list`, `index_adjacency_list`, `bidirectional_adjacency_list`, `index_bidirectional_adjacency_list`, `mapped_adjacency_list`, `mapped_bidirectional_adjacency_list` | +| `descriptor.hpp` | Single `descriptor` (129 lines) | `vertex_descriptor` + `edge_descriptor` with exposition-only internals (Q1) | +| `descriptor_view.hpp` | `descriptor_view` + `descriptor_subrange_view` (11 lines) | `vertex_descriptor_view` + `edge_descriptor_view` | +| `edgelist_concepts.hpp` | 1-arg `sourced_edgelist`, `sourced_index_edgelist`, `has_edge_value` | 2-arg `basic_sourced_edgelist`, `basic_sourced_index_edgelist`, `has_edge_value` | +| `edgelist_types.hpp` | 1-arg type aliases, includes `edge_reference_t` | 2-arg type aliases, remove `edge_reference_t`, add `raw_vertex_id_t` | +| `edgelist_typetraits.hpp` | `is_directed` only | Same (no change needed) | + +### New files to add + +| File | Content | +|------|---------| +| `descriptor_traits.hpp` | `is_vertex_descriptor`, `is_edge_descriptor`, `is_descriptor`, `vertex_descriptor_type`, `edge_descriptor_type`, `random_access_descriptor`, `iterator_based_descriptor` — modelled on `graph-v3/include/graph/adj_list/descriptor_traits.hpp` | + +### Procedure + +1. Delete `concepts_edges_before.hpp` and `concepts_basic_adj_list.hpp`. +2. Rewrite the 9 files listed above, modelling each on the corresponding graph-v3 header + but simplified to exposition-only level (Q1). Use `// For exposition only` comment + convention matching the existing style. +3. Create `descriptor_traits.hpp`. +4. Update any `\lstinputlisting` directives in `container_interface.tex` that reference + deleted files — either remove the directive or redirect it. +5. **Verify:** `make D3130_Container_Interface` builds. +6. **Commit:** `"Phase A: Rewrite D3130 source files to match graph-v3"` + +### Safety notes + +- The LaTeX `\lstinputlisting` directives reference files by exact name. Deleting a file + without removing its `\lstinputlisting` will break the build. Step 4 handles this. +- The new source files are the foundation for all subsequent phases. Getting them right + means the concept / type alias sections can reference them directly. + +--- + +## Phase B — Type Alias & Traits Tables + +**Goal:** Update Tables 2 (type aliases) and the traits table to match the implementation. + +### Type alias table changes (Table `graph_type`) + +| Action | Row(s) | +|--------|--------| +| Remove | `graph_reference_t`, `vertex_reference_t`, `edge_reference_t` | +| Update | `vertex_edge_range_t` → note as alias for `out_edge_range_t` | +| Update | `vertex_edge_iterator_t` → note as alias for `out_edge_iterator_t` | +| Update | `edge_t` → note as alias for `out_edge_t`, now a descriptor type | +| Update | `vertex_id_t` → `remove_cvref_t))>` | +| Add | `out_edge_range_t` = `decltype(out_edges(g, vertex_t))` | +| Add | `out_edge_iterator_t` = `iterator_t>` | +| Add | `out_edge_t` = `range_value_t>` | +| Add | `in_edge_range_t`, `in_edge_iterator_t`, `in_edge_t` (analogous) | +| Add | `vertex_id_store_t` — normative (Q5) | +| Add | *`raw-vertex-id-type`* — exposition-only italic (Q5) | + +### Traits table changes (Table `graph_traits`) + +| Action | Row(s) | +|--------|--------| +| Remove | `define_unordered_edge`, `unordered_edge`, `ordered_edge` | +| Keep | `define_adjacency_matrix`, `is_adjacency_matrix`/`_v`, `adjacency_matrix` (Q4) — add note that reference implementation has not yet added support | +| Update | `has_contains_edge` → `has_contains_edge` (second template parameter) | +| Add | `has_in_degree` / `_v` | +| Add | `has_find_in_edge` / `_v` | +| Add | `has_contains_in_edge` / `_v` | +| Add | `has_basic_queries` / `_v` — `degree + find_vertex + find_vertex_edge` | +| Add | `has_full_queries` / `_v` — `has_basic_queries + contains_edge` | + +### Procedure + +1. Edit the type alias `\begin{tabular}` block in `container_interface.tex` (~line 143–170). +2. Edit the traits `\begin{tabular}` block (~line 99–120). +3. **Verify:** `make D3130_Container_Interface` builds. +4. **Commit:** `"Phase B: Update type alias and traits tables"` + +--- + +## Phase C — Function Tables & CPOs + +**Goal:** Update the three function tables (Graph, Vertex, Edge) and the edgelist function +table to match the implementation's CPO names and signatures. + +### Graph function table changes (Table `graph_func`) + +| Action | Row | +|--------|-----| +| Rename | `has_edge(g)` → `has_edges(g)` | +| Update | `num_edges(g)` default uses `out_edges(g,u)` | + +### Vertex function table changes (Table `vertex_func`) + +| Action | Row | +|--------|-----| +| Rename | `edges(g,u)` → `out_edges(g,u)` (note `edges` as backward-compatible alias) | +| Rename | `edges(g,uid)` → `out_edges(g,uid)` | +| Rename | `degree(g,u)` → `out_degree(g,u)` (note `degree` as alias) | +| Rename | `degree(g,uid)` → `out_degree(g,uid)` | +| Add | `in_edges(g,u)` / `in_edges(g,uid)` | +| Add | `in_degree(g,u)` / `in_degree(g,uid)` | +| Update | All `vertex_edge_range_t` references → `out_edge_range_t` | + +### Edge function table changes (Table `edge_func`) + +| Action | Row | +|--------|-----| +| Rename | `find_vertex_edge(g,u,vid)` → `find_out_edge(g,u,vid)` | +| Rename | `find_vertex_edge(g,uid,vid)` → `find_out_edge(g,uid,vid)` | +| Rename | `contains_edge(g,uid,vid)` → `contains_out_edge(g,uid,vid)` | +| Update | `source_id(g,uv)` — remove "n/a, optional", make mandatory for descriptor-based edges (Q8) | +| Add | `source(g,uv)` CPO | +| Add | `find_in_edge(g,u,vid)`, `find_in_edge(g,uid,vid)`, `find_in_edge(g,u,v)` | +| Add | `contains_in_edge(g,u,vid)`, `contains_in_edge(g,uid,vid)` | +| Update | Remove the `\hdashline` / "only available when optional `source_id`" note | +| Update | All `edge_reference_t` in default implementations → `edge_t` | +| Update | Pass descriptors by value in signatures (Q2) | + +### Edgelist function table changes (Table `edgelist_func`) + +| Action | Row | +|--------|-----| +| Update | `target_id(e)` → `target_id(el, uv)` (2-arg) | +| Update | `source_id(e)` → `source_id(el, uv)` (2-arg) | +| Update | `edge_value(e)` → `edge_value(el, uv)` (2-arg) | +| Update | `contains_edge` lambda: `edge_reference_t` → raw element type | + +### Procedure + +1. Edit Graph function table (~line 195–207). +2. Edit Vertex function table (~line 217–235). +3. Edit Edge function table (~line 248–280). +4. Edit Edgelist function table (~line 489–505). +5. Update prose between/after tables that references old CPO names. +6. **Verify:** `make D3130_Container_Interface` builds. +7. **Commit:** `"Phase C: Update function tables and CPO names"` + +--- + +## Phase D — Concept Sections + +**Goal:** Rewrite the concept subsections (§1.1–§1.3 and §2.2) to reference the new source +files and explain the simplified concept hierarchy. + +### Adjacency list concept sections + +| Section | Change | +|---------|--------| +| §1.1.1 "Edge Concepts" | Reference new `concepts_edges.hpp` (single `edge`). Update the prose explaining why return types are not validated — still applies. Remove the `concepts_edges_before.hpp` reference. | +| §1.1.1 "Edge Range Concepts" | Reference new `concepts_target_edge_range.hpp` (now `out_edge_range` + `in_edge_range`). Update prose. | +| §1.1.2 "Vertex Concepts" | Reference new `concepts_vertex_range.hpp` (now with `vertex`, `mapped_vertex_range`). | +| §1.1.3 "Adjacency List Concepts" | Reference new `concepts_adj_list.hpp`. Remove the `\phil` verification note. Remove the `\lstinputlisting` for `concepts_basic_adj_list.hpp`. Explain the removal of `basic_*`/`sourced_*` variants — descriptor design makes them unnecessary. | +| §2.2 "Edgelist Concepts" | Reference new `edgelist_concepts.hpp`. Explain 2-arg form. | + +### Procedure + +1. Update `\lstinputlisting` directives (some files renamed/deleted in Phase A). +2. Rewrite surrounding prose for each concept subsection. +3. **Verify:** `make D3130_Container_Interface` builds. +4. **Commit:** `"Phase D: Rewrite concept sections"` + +--- + +## Phase E — Descriptor Section Rewrite + +**Goal:** Replace the "Vertex and Edge Descriptor Views" subsection (~line 362–410) with a +comprehensive descriptor exposition that includes the "Why Descriptors?" motivation (§0.2a +of the strategy), the comparison table, the split descriptor architecture, pass-by-value +convention (Q2), and the exposition-only styling (Q1). + +### Content outline for the new section + +1. **Motivation** — Why descriptors? Comparison table (iterators vs descriptors). Implicit + vertex support. Automatic pattern recognition. +2. **Descriptor types** — `vertex_descriptor` and + `edge_descriptor` with exposition-only data members. + Observable interface: construction, `vertex_id()`, comparison, increment. +3. **Descriptor traits** — `\lstinputlisting{descriptor_traits.hpp}`. Brief explanation. +4. **Descriptor views** — `vertex_descriptor_view` and `edge_descriptor_view`. + `\lstinputlisting{descriptor_view.hpp}`. +5. **Pass-by-value convention** — Descriptors are ≤16 bytes, trivially copyable, passed by + value in CPO signatures. `const&` only in `requires` clauses. + +### Procedure + +1. Replace the existing "Vertex and Edge Descriptor Views" subsection prose. +2. Update `\lstinputlisting{descriptor.hpp}` to show the new split descriptors. +3. Add `\lstinputlisting{descriptor_traits.hpp}`. +4. Update `\lstinputlisting{descriptor_view.hpp}`. +5. **Verify:** `make D3130_Container_Interface` builds. +6. **Commit:** `"Phase E: Rewrite descriptor section with motivation and split architecture"` + +--- + +## Phase F — Edgelist Section + +**Goal:** Update the entire Edgelist Interface section (§2) to match the implementation. + +### Changes + +| Subsection | Change | +|------------|--------| +| §2 intro | Update `source_id(e)` → `source_id(el, uv)` prose | +| §2.1 Namespace | Change `std::graph::edgelist` → `std::graph::edge_list` (Q6). Remove the `\phil` merge note on line 417. | +| §2.2 Concepts | Already handled by Phase D (just verify) | +| §2.3 Traits | No change (edgelist traits unchanged) | +| §2.4 Types | Update type alias table (Table `edgelist_type`): remove `edge_reference_t`, update `edge_value_t` and `vertex_id_t` to 2-arg form, add `raw_vertex_id_t` | +| §2.5 Functions | Already handled by Phase C (just verify) | +| §2.6 Pattern section | Rename `edge_info` → `edge_data` (4 occurrences). Rename `vertex_info` → `vertex_data`. Add `pair` pattern. Add `edge_data` reference variants. | + +### Procedure + +1. Update namespace prose and remove `\phil` note. +2. Update edgelist type alias table. +3. Rename `edge_info` → `edge_data` in pattern section prose. +4. **Verify:** `make D3130_Container_Interface` builds. +5. **Commit:** `"Phase F: Update edgelist section — namespace, 2-arg CPOs, edge_data patterns"` + +--- + +## Phase G — New Sections + +**Goal:** Add content for new items that have no existing LaTeX counterpart. + +### New subsections to add + +| Subsection | Location | Content | +|------------|----------|---------| +| §1.4 "Classes and Structs" expansion | After existing `graph_error` | Add `vertex_data`, `edge_data`, `neighbor_data` struct families with specialization tables. Add `copyable_vertex_t`, `copyable_edge_t`, `copyable_neighbor_t` aliases. | +| §1.5 "Value Function Concepts" | New subsection after Classes | `vertex_value_function` and `edge_value_function` — from `graph_concepts.hpp` | +| §1.6 "Vertex Property Map" | New subsection | `vertex_property_map` type alias, `make_vertex_property_map`, `vertex_property_map_contains`, `vertex_property_map_get` (Q7) | +| §1.7 "Vertex ID Determination" update | Existing subsection | Rewrite to descriptor-based approach (pattern detection on descriptors, not on nested range types) | + +### Procedure + +1. Add each new subsection to `container_interface.tex`. +2. Adjust section numbering / labels as needed. +3. **Verify:** `make D3130_Container_Interface` builds. +4. **Commit:** `"Phase G: Add data structs, value function concepts, vertex property map"` + +--- + +## Phase H — Prose Audit & Cleanup + +**Goal:** Systematic search-and-replace pass through all D3130 text, plus cleanup of stale +commentary, and revision history update. + +### Search-and-replace pass + +| Old term | New term | Expected locations | +|----------|----------|--------------------| +| `vertex_reference_t` | `vertex_t` | Prose, concept definitions, function sigs | +| `edge_reference_t` | `edge_t` | Same | +| `vertex_info` | `vertex_data` | Verify none remain in D3130 (expected 0) | +| `edge_info` | `edge_data` | Edgelist pattern section (4 occurrences in D3130) | +| `targeted_edge` | `edge` | Concept references in prose | +| `sourced_edge` | (remove or replace) | Concept references in prose | +| `basic_*` concepts | (remove references) | Prose | +| `std::graph::edgelist` | `std::graph::edge_list` | Namespace references | +| `edges(g,u)` (as primary name) | `out_edges(g,u)` | Prose (function tables handled in Phase C) | +| `degree(g,u)` (as primary name) | `out_degree(g,u)` | Prose | +| `find_vertex_edge` | `find_out_edge` | Prose | +| `contains_edge` (adj list) | `contains_out_edge` | Prose | +| `has_edge(g)` | `has_edges(g)` | Prose | +| `inner_value()` | `underlying_value(container)` / `inner_value(container)` | Descriptor prose | + +### Additional cleanup + +1. Update adjacency matrix prose — revise `contains_out_edge` discussion to note O(1) + guarantee when `adjacency_matrix` is satisfied (Q4). Note that the reference + implementation has not yet added adjacency matrix support. +2. Review "Using Existing Data Structures" section for accuracy. +3. Remove any remaining `\phil` notes. +4. Update `D3130_Container_Interface/tex/revision.tex` with a revision entry summarizing + the changes. + +### Procedure + +1. Run `grep -n` for each old term to find all occurrences. +2. Edit each occurrence. +3. Review adjacency matrix prose. +4. Review "Using Existing Data Structures" section. +5. Remove stale `\phil` notes. +6. Update revision history. +7. **Verify:** `make D3130_Container_Interface` builds. +8. **Commit:** `"Phase H: Prose audit — rename all obsolete types, update revision history"` + +--- + +## Phase I — Implementation Changes (graph-v3) + +**Goal:** Update the graph-v3 implementation to match Q3 (drop trait gate on `edge`) +and ensure consistency with the paper. + +### I.1 — Drop `is_edge_descriptor_v` gate on `edge` + +**File:** `include/graph/adj_list/adjacency_list_concepts.hpp`, line 46 + +**Current:** +```cpp +template +concept edge = is_edge_descriptor_v> && requires(G& g, const E& e) { + source_id(g, e); + source(g, e); + target_id(g, e); + target(g, e); +}; +``` + +**New:** +```cpp +template +concept edge = requires(G& g, const E& e) { + source_id(g, e); + target_id(g, e); +}; +``` + +**Impact assessment:** +- Removes the trait gate — any type satisfying `source_id`/`target_id` CPOs qualifies. +- `source(g,e)` and `target(g,e)` are dropped from the concept requirement. Algorithms that + need vertex descriptors (not just IDs) should add those requirements directly. +- Tests in `test_descriptor_traits.cpp` test the *trait* (`is_edge_descriptor_v`), not the + *concept* — they remain valid. +- Tests in `test_target_id_cpo.cpp` use `requires(is_edge_descriptor_v)` as a guard — + these remain valid since the trait still exists; it's just no longer in the concept. +- Algorithms using `edge` in `requires` clauses will accept a broader set of types. + Need to run full test suite to verify no regressions. + +### I.2 — Verify edge_list interop + +After dropping the trait gate, edge_list raw elements (tuples, pairs, `edge_data`) should +be testable against `edge`. Add a static assertion test: + +```cpp +// In a new or existing edge_list test: +using EL = std::vector>; +static_assert(edge>); +``` + +### I.3 — Algorithms that need `source(g,e)` / `target(g,e)` + +Search all algorithm headers for uses of `source(g,` and `target(g,` to identify any that +need the vertex-descriptor operations. These algorithms should add explicit requirements +beyond `edge`: + +```cpp +template + requires edge && requires(G& g, const E& e) { source(g, e); target(g, e); } +void algorithm_needing_vertex_descriptors(G& g, E e); +``` + +### Procedure + +1. Enter graph-v3 workspace root (`/home/phil/dev_graph/graph-v3`). +2. Ensure build directory is configured: + `cmake -S . -B build/linux-gcc-debug -DCMAKE_BUILD_TYPE=Debug` (if needed). +3. Edit `adjacency_list_concepts.hpp` line 46. +4. Build: `cmake --build build/linux-gcc-debug`. +5. Run tests: `ctest --test-dir build/linux-gcc-debug`. +6. If tests fail, analyse each failure: + - If an algorithm relied on `source(g,e)` / `target(g,e)` being in the concept, add + explicit requirements to that algorithm. + - If a test assumed `edge` requires `is_edge_descriptor_v`, update the test. +7. Add edge_list interop static assertion test. +8. Run tests again to confirm green. +9. **Commit (graph-v3):** `"Relax edge concept — drop is_edge_descriptor_v gate (Q3)"` + +### Safety notes + +- This is the **only** implementation change required by the Q1-Q8 decisions. All other Qs + affect only the paper text. +- The trait `is_edge_descriptor_v` itself is NOT removed — it remains available for code + that specifically needs to detect adj_list descriptors. Only its role as a *concept gate* + is eliminated. +- If test failures are extensive, this phase can be deferred and the paper updated + independently. The paper should specify the relaxed concept regardless of whether the + implementation has caught up. + +--- + +## Phase J — Cross-Paper Fixups (Allowed Scope) + +**Goal:** Update references to changed D3130 types in the algorithms paper and shared +specification text, while keeping this plan within the approved scope. + +### Affected files + +| File | Occurrences | Changes | +|------|-------------|---------| +| `tex/conventions.tex` | 4 | `vertex_reference_t` → `vertex_t`, `edge_reference_t` → `edge_t`, `vertex_info` → `vertex_data`, `edge_info` → `edge_data` in projection function descriptions | +| `tex/specification.tex` | 6 | `vertex_reference_t` → `vertex_t` in function signatures and concept definitions | +| `D3128_Algorithms/tex/algorithms.tex` | 2 | `edge_reference_t` → `edge_t` in weight function concept | +| `D3128_Algorithms/tex/revision.tex` | 2 | Update revision note referencing the type changes | + +### Procedure + +1. Fix `tex/conventions.tex` (4 occurrences). +2. Fix `tex/specification.tex` (6 occurrences). +3. Fix `D3128_Algorithms/tex/algorithms.tex` (2 occurrences). +4. Fix `D3128_Algorithms/tex/revision.tex` (2 occurrences). +5. **Verify:** `make D3128_Algorithms` and `make` for shared top-level docs affected. +6. **Commit:** `"Phase J: Cross-paper fixups for algorithms and shared spec text"` + +### Optional extension (separate plan) + +If broader editorial synchronization is desired, create a dedicated Views-paper plan for +`D3129_Views/tex/views.tex` and `D3129_Views/tex/revision.tex` (large-volume rename and +potential source-listing impacts). + +### Safety notes + +- For bulk replacements, avoid relying on `sed` word-boundary `\b` behavior (tool-dependent). + Prefer `perl -pe` with explicit token boundaries, then spot-check all replacements. +- Keep this phase scoped to Algorithms + shared specification files. Treat Views-paper + renames and any `\lstinputlisting` source-file rename fallout as a separate effort. + +--- + +## Phase dependency diagram + +``` +Phase A (source files) + ├── Phase B (type alias & traits tables) + │ └── Phase C (function tables & CPOs) + │ └── Phase F (edgelist section) ← also needs A + ├── Phase D (concept sections) + │ └── Phase G (new sections) ← also needs B + └── Phase E (descriptor section) + +All A–G ──→ Phase H (prose audit & cleanup) + └── Phase J (cross-paper fixups) + +Phase I (implementation) ──→ independent +``` + +--- + +## Risk register + +| Risk | Likelihood | Impact | Mitigation | +|------|------------|--------|------------| +| `\lstinputlisting` broken by file rename/delete | High (if not careful) | Build failure | Phase A explicitly handles all directives. Verify build after. | +| Bulk rename introduces false positives | Medium | Incorrect prose | Prefer `perl -pe` with explicit token boundaries; then spot-check every replacement. | +| Dropping trait gate breaks graph-v3 tests | Medium | Test failures | Run full test suite in Phase I. If failures are extensive, defer. | +| Section numbering drift after adding new sections | Low | LaTeX warnings | Use `\label` / `\ref` for all cross-references. | +| Adjacency matrix traits have no implementation backing | Low | Reviewer confusion | Add explicit note in traits table: "Reference implementation pending." | +| Cross-paper edits create merge conflicts with other branches | Medium | Git conflicts | Phase J is last and on its own commit, easy to rebase. | diff --git a/agents/gci_update_strategy.md b/agents/gci_update_strategy.md new file mode 100644 index 0000000..5008213 --- /dev/null +++ b/agents/gci_update_strategy.md @@ -0,0 +1,1237 @@ +# GCI Document Update Strategy + +## Purpose + +This document captures all discrepancies between the **D3130 Container Interface** LaTeX +proposal and the **graph-v3** reference implementation in `graph-v3/include/graph/`, along +with a concrete plan to bring the LaTeX documents into alignment with the implementation. + +**The implementation is the definitive/authoritative source.** Where the LaTeX and the +implementation disagree, the implementation is correct and the LaTeX must be updated. + +### Relationship to `algo_update_strategy.md` + +The algorithm strategy document (§0) describes the fundamental shift from reference-based +types (`vertex_reference_t`, `edge_reference_t`, `vertex_info`, `edge_info`) to +value-based descriptors (`vertex_t`, `edge_t`, `vertex_data`, `edge_data`). +`vertex_info` and `edge_info` were **renamed** to `vertex_data` and `edge_data` to +clarify that they represent the vertex and edge data models, respectively. That +shift **originates** in the GCI — it is the GCI that defines these types. This document +details the GCI-specific changes; the algorithm document consumes the types defined here. + +Note: The algorithms have not yet been updated to work with vertices stored in mapped +(key-based) containers. The mapped container concepts and property maps are infrastructure +defined in the GCI for future algorithm support. + +--- + +## 0. Fundamental Design Shift: Descriptor-Based Architecture + +The most significant change in the GCI is the replacement of the old `descriptor` class +(a single monolithic class parameterized on `InnerIter`) with a **split descriptor +architecture** consisting of separate vertex and edge descriptor classes, each with a +corresponding descriptor view. + +### 0.1 Old Design (LaTeX / D3130) + +The LaTeX defines a **single** `descriptor` class in +`D3130_Container_Interface/src/descriptor.hpp`: +- Parameterized on `forward_iterator InnerIter` +- Stores either an index (`ptrdiff_t`) or an iterator, depending on `random_access_iterator` +- Full operator suite: `++`, `--`, `+=`, `-=`, `+`, `-`, `[]`, `==`, `<=>` +- `inner_value()` member to access the underlying container element +- `descriptor_view(R&&)` and `descriptor_subrange_view(R&&, R&&)` factory functions + +### 0.2 New Design (Implementation) + +The implementation replaces the single class with **four distinct types** in the +`graph::adj_list` namespace: + +| New Type (Implementation) | Purpose | File | +| ------------------------------------------------------------ | ---------------------------------------- | ---------------------------- | +| `vertex_descriptor` | Lightweight vertex handle | `vertex_descriptor.hpp` | +| `edge_descriptor` | Lightweight edge handle with source info | `edge_descriptor.hpp` | +| `vertex_descriptor_view` | View over vertex container | `vertex_descriptor_view.hpp` | +| `edge_descriptor_view` | View over edge container for one vertex | `edge_descriptor_view.hpp` | + +Key design differences: +- **`vertex_descriptor`** uses `storage_type = conditional_t` + (note: `size_t`, not `ptrdiff_t`) +- **`edge_descriptor`** stores both the edge position (`edge_storage_type`) and the source + vertex (`vertex_desc`), enabling `source_id()` without external lookup +- **`EdgeDirection`** tag class (`out_edge_tag` / `in_edge_tag`) distinguishes outgoing from + incoming edge descriptors, enabling bidirectional graph support +- Inner value access is via `underlying_value(container)` and `inner_value(container)`, not + `inner_value()` — the container must be passed in +- `vertex_id()` is a member of `vertex_descriptor` (not just a free function) + +### 0.2a Why Descriptors? (Must Include in Paper) + +The graph-v3 documentation (`docs/user-guide/adjacency-lists.md`, §6) contains an excellent +motivation for the descriptor design that **must** be captured in the D3130 paper. The paper +already has a prose paragraph on descriptors (§"Vertex and Edge Descriptor Views") but it +lacks the crisp comparison table and the key insight about implicit vertices. + +**Key points to incorporate into D3130:** + +1. **Abstraction** — A single descriptor type can represent both an index into a `vector` and + an iterator into a `map`. This means the library needs far fewer concept definitions and + function overloads than an iterator-centric or reference-centric design. + +2. **Implicit vertex support** — Index-based vertex descriptors can refer to a vertex even + when no physical vertex object exists (e.g., `compressed_graph` or an implicit grid). The + vertex is identified purely by its index, so algorithms and views work the same way + regardless of whether the container materializes vertex storage. + +3. **Comparison table** — The following table should appear in the paper's descriptor section: + + | Concern | Raw iterators | Descriptors | + | ------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------ | + | Abstraction | Expose container layout | Hide storage strategy — one concept covers indexed and keyed graphs | + | Overload reduction | Separate overloads for index vs. iterator | A single overload accepts the descriptor | + | Invalidation | Invalidated by container mutation | Can be re-resolved via `find_vertex` | + | Copyability | Varies (e.g., `forward_list::iterator`) | Always trivially copyable or cheap | + | Size | May be pointer-sized or larger | Index-based: one integer | + +4. **Automatic pattern recognition** — CPOs detect containers automatically: + - **Random-access** containers (`vector`, `deque`) → index-based IDs + - **Associative** containers (`map`, `unordered_map`) → key-based IDs + - **Edge patterns** → `int`, `pair`, `tuple`, or structs are recognized + to automatically extract edge properties (target ID, edge value, etc.) + +**Source:** `graph-v3/docs/user-guide/adjacency-lists.md`, §6 "Descriptors" (lines 276–345). + +**Action:** When rewriting the "Vertex and Edge Descriptor Views" section of +`container_interface.tex` (Phase 1, task 4), incorporate this motivation material prominently. +The comparison table and implicit-vertex insight belong near the beginning of the descriptor +discussion, before the type definitions. + +### 0.3 Descriptor Traits (New) + +The implementation adds a comprehensive trait system not in the LaTeX: + +| Trait / Concept | Purpose | +| -------------------------------------------- | ------------------------------------------------ | +| `is_vertex_descriptor` / `_v` | Detect vertex descriptors | +| `is_edge_descriptor` / `_v` | Detect edge descriptors | +| `is_in_edge_descriptor` / `_v` | Detect incoming edge descriptors | +| `is_descriptor` / `_v` | Either vertex or edge descriptor | +| `is_vertex_descriptor_view` / `_v` | Detect vertex descriptor views | +| `is_edge_descriptor_view` / `_v` | Detect edge descriptor views | +| `vertex_descriptor_type` (concept) | Constrained concept for vertex descriptors | +| `edge_descriptor_type` (concept) | Constrained concept for edge descriptors | +| `descriptor_type` (concept) | Either descriptor type | +| `random_access_descriptor` (concept) | Descriptor with index-based storage | +| `iterator_based_descriptor` (concept) | Descriptor with iterator-based storage | + +### 0.4 Descriptor Pattern Detection (New) + +The implementation defines concepts for detecting edge and vertex patterns: + +**Vertex patterns:** +- `direct_vertex_type` — random-access (ID = index) +- `keyed_vertex_type` — bidirectional + pair-like (ID = key, for maps) +- `random_access_vertex_pattern` — returns whole container element +- `pair_value_vertex_pattern` — returns `.second` (for maps) +- `whole_value_vertex_pattern` — returns whole dereferenced value + +**Edge patterns:** +- `simple_edge_type` — integral type (just target ID) +- `pair_edge_type` — pair with `.first`=target, `.second`=properties +- `tuple_edge_type` — tuple with first element = target +- `custom_edge_type` — custom struct (user manages) +- `edge_value_type` — union of all above + +**Action:** Rewrite `D3130_Container_Interface/src/descriptor.hpp` and +`D3130_Container_Interface/src/descriptor_view.hpp` to reflect the split descriptor +architecture. Add descriptor traits and pattern detection concepts to the proposal. + +--- + +## 1. Concept Discrepancies + +### 1.1 Edge Concepts — `edge_reference_t` → `edge_t` + +| Aspect | LaTeX (D3130) | Implementation | +| --------------------- | ---------------------------------------------------- | --------------------------------------------------------------------- | +| Edge parameter type | `edge_reference_t uv` | `const E& e` where `E` satisfies `edge_descriptor_type` | +| `targeted_edge` | `requires(G&& g, edge_reference_t uv)` | Subsumed by `edge` concept on edge descriptors | +| `sourced_edge` | `requires(G&& g, edge_reference_t uv)` | Subsumed by `edge` concept (all descriptors have source info) | +| `sourced_targeted_edge` | Compound concept | Replaced by single `edge` concept | +| Unified concept | 3 separate edge concepts + 3 basic variants | Single `edge` concept requiring all operations | + +**Implementation's `edge` concept** (from `adjacency_list_concepts.hpp`): +```cpp +template +concept edge = is_edge_descriptor_v> && requires(G& g, const E& e) { + target_id(g, e); target(g, e); + source_id(g, e); source(g, e); +}; +``` + +The `basic_*` variants (`basic_targeted_edge`, `basic_sourced_edge`, etc.) in +`concepts_edges_before.hpp` no longer exist in the implementation. + +**Action:** +1. Replace all `targeted_edge`, `sourced_edge`, `sourced_targeted_edge`, `basic_targeted_edge`, + `basic_sourced_edge`, `basic_sourced_targeted_edge` concepts with the single `edge`. +2. Remove `concepts_edges.hpp` and `concepts_edges_before.hpp` source files. +3. Add the `edge` concept to the proposal. + +### 1.2 Edge Range Concepts + +| Aspect | LaTeX (D3130) | Implementation | +| -------------------------- | ------------------------------------ | ------------------------------------------------------- | +| `targeted_edge_range` | Uses `basic_targeted_edge_range` | Replaced by `out_edge_range` | +| `basic_targeted_edge_range`| Intermediate concept | Does not exist | +| Out-edge range | `targeted_edge_range` | `out_edge_range`: forward_range + edge | +| In-edge range | Not in LaTeX | `in_edge_range`: forward_range + edge | + +**Implementation:** +```cpp +template +concept out_edge_range = forward_range && edge>; + +template +concept in_edge_range = forward_range && edge>; +``` + +**Action:** Replace `targeted_edge_range` and `basic_targeted_edge_range` with +`out_edge_range`. Add `in_edge_range`. Remove `concepts_target_edge_range.hpp`. + +### 1.3 Vertex Concepts + +| Aspect | LaTeX (D3130) | Implementation | +| --------------------- | ------------------------------------------------- | --------------------------------------------------------- | +| `_common_vertex_range`| Uses `vertex_iterator_t` | Not present; replaced by `vertex` concept | +| `vertex_range` | `forward_range>` | `forward_range && sized_range && vertex>` | +| `index_vertex_range` | `random_access_range> && integral>` | Checks `integral>` AND `integral::storage_type>` | +| `vertex` | Not in LaTeX | New concept: `is_vertex_descriptor_v` + vertex_id/find_vertex | +| `mapped_vertex_range` | Not in LaTeX | New: `!index_vertex_range && hashable_vertex_id` | + +**Implementation's `vertex` concept:** +```cpp +template +concept vertex = is_vertex_descriptor_v> && + requires(G& g, const V& u) { + vertex_id(g, u); + find_vertex(g, vertex_id(g, u)); + }; +``` + +**Action:** +1. Replace `_common_vertex_range` with the `vertex` concept. +2. Update `vertex_range` and `index_vertex_range` definitions. +3. Add `mapped_vertex_range` concept. +4. Remove `concepts_vertex_range.hpp`, replace with new source file. + +### 1.4 Adjacency List Concepts + +| Aspect | LaTeX (D3130) | Implementation | +| -------------------------------------- | -------------------------------------- | ------------------------------------------- | +| `adjacency_list` | `vertex_range && targeted_edge_range && targeted_edge` | `requires { vertices(g) } -> vertex_range; { out_edges(g,u) } -> out_edge_range` | +| `index_adjacency_list` | `index_vertex_range && targeted_edge_range && targeted_edge` | `adjacency_list && index_vertex_range` | +| `sourced_adjacency_list` | Present | **Removed** — source info is always available via descriptors | +| `sourced_index_adjacency_list` | Present | **Removed** | +| `basic_adjacency_list` | Present (in concepts_basic_adj_list.hpp) | **Removed** | +| `basic_index_adjacency_list` | Present | **Removed** | +| `basic_sourced_adjacency_list` | Present | **Removed** | +| `basic_sourced_index_adjacency_list` | Present | **Removed** | +| `ordered_vertex_edges` | Not in LaTeX | New concept for sorted adjacency lists | +| `bidirectional_adjacency_list` | Not in LaTeX | New: adjacency_list + in_edges support | +| `index_bidirectional_adjacency_list` | Not in LaTeX | New: bidirectional + index vertices | +| `hashable_vertex_id` | Not in LaTeX | New: vertex IDs usable as unordered_map keys | +| `mapped_adjacency_list` | Not in LaTeX | New: adjacency_list + mapped_vertex_range | +| `mapped_bidirectional_adjacency_list` | Not in LaTeX | New: bidirectional + mapped vertices | + +**Action:** +1. Remove all `basic_*` and `sourced_*` adjacency list concepts — the descriptor design makes + "sourced" vs. "unsourced" distinction unnecessary (all edge descriptors carry source info + via the source vertex_descriptor stored in the edge_descriptor). +2. Remove `concepts_basic_adj_list.hpp` source file. +3. Add `ordered_vertex_edges`, `bidirectional_adjacency_list`, + `index_bidirectional_adjacency_list`, `hashable_vertex_id`, `mapped_vertex_range`, + `mapped_adjacency_list`, `mapped_bidirectional_adjacency_list` concepts. +4. Rewrite `concepts_adj_list.hpp` to match the implementation. + +--- + +## 2. Type Alias Discrepancies + +### 2.1 Type Aliases Table + +The LaTeX Type Aliases table (Table 2) needs significant updates: + +| Type Alias | LaTeX (D3130) | Implementation | Change | +| ----------------------- | ---------------------------------------------- | -------------------------------------------------------- | ------ | +| `vertex_range_t` | `decltype(vertices(g))` | Same | OK | +| `vertex_iterator_t` | `iterator_t>` | Same | OK | +| `vertex_t` | `range_value_t>` | Same, but now a `vertex_descriptor` type — **replaces `vertex_reference_t`** | Clarify | +| `vertex_reference_t` | `range_reference_t>` | **Removed** — replaced by `vertex_t` (a descriptor) | Remove | +| `vertex_id_t` | `decltype(vertex_id(g,u))` | `remove_cvref_t))>` | Update | +| `vertex_value_t` | `decltype(vertex_value(g,u))` | Same (via CPO) | OK | +| `vertex_edge_range_t`| `decltype(edges(g,u))` | `out_edge_range_t` (alias) | Update | +| `vertex_edge_iterator_t` | `iterator_t>` | `out_edge_iterator_t` (alias) | Update | +| `edge_t` | `range_value_t>` | `out_edge_t` (alias); now `edge_descriptor<...>` type — **replaces `edge_reference_t`** | Clarify | +| `edge_reference_t` | `range_reference_t>` | **Removed** — replaced by `edge_t` (a descriptor) | Remove | +| `edge_value_t` | `decltype(edge_value(g,uv))` | Same (via CPO) | OK | +| `graph_reference_t` | `add_lvalue_reference` | Not in implementation | Review | +| `graph_value_t` | `decltype(graph_value(g))` | Same (via CPO) | OK | +| (new) `raw_vertex_id_t` | Not in LaTeX | `decltype(vertex_id(g, vertex_t))` — preserves references | Add | +| (new) `out_edge_range_t` | Not in LaTeX | `decltype(out_edges(g, vertex_t))` | Add | +| (new) `out_edge_iterator_t` | Not in LaTeX | `iterator_t>` | Add | +| (new) `out_edge_t` | Not in LaTeX | `range_value_t>` | Add | +| (new) `in_edge_range_t` | Not in LaTeX | `decltype(in_edges(g, vertex_t))` | Add | +| (new) `in_edge_iterator_t` | Not in LaTeX | `iterator_t>` | Add | +| (new) `in_edge_t` | Not in LaTeX | `range_value_t>` | Add | + +**Action:** Remove `vertex_reference_t`, `edge_reference_t`, and `graph_reference_t`. +Every use of `vertex_reference_t` is replaced by `vertex_t` and every use of +`edge_reference_t` is replaced by `edge_t` — both are now lightweight descriptor +types (value types, not references). Add the `out_edge_*`, `in_edge_*`, and +`raw_vertex_id_t` aliases. Document that `vertex_edge_range_t`, `vertex_edge_iterator_t`, +and `edge_t` are backward-compatibility aliases for `out_edge_range_t`, +`out_edge_iterator_t`, and `out_edge_t`. + +--- + +## 3. Traits Table Discrepancies + +### 3.1 Traits Table + +| Trait (LaTeX) | Implementation Status | Change | +| ------------------------------------------- | ------------------------------------------- | ------ | +| `has_degree` | Present (concept + `_v`) | OK | +| `has_find_vertex` | Present (concept + `_v`) | OK | +| `has_find_vertex_edge` | Present (concept + `_v`) | OK | +| `has_contains_edge` | Present (concept + `_v`) — now `has_contains_edge` | Update (added V param) | +| `define_unordered_edge` | **Removed** — not in implementation | Remove | +| `unordered_edge` | **Removed** — not in implementation | Remove | +| `ordered_edge` | **Removed** — not in implementation | Remove | +| `define_adjacency_matrix` | **Removed** — not in implementation | Remove | +| `is_adjacency_matrix` / `_v` | **Removed** — not in implementation | Remove | +| `adjacency_matrix` | **Removed** — not in implementation | Remove | +| (new) `has_in_degree` / `_v` | New in implementation | Add | +| (new) `has_find_in_edge` / `_v` | New in implementation | Add | +| (new) `has_contains_in_edge` / `_v` | New in implementation | Add | +| (new) `has_basic_queries` / `_v` | New: degree+find_vertex+find_vertex_edge | Add | +| (new) `has_full_queries` / `_v` | New: basic_queries+contains_edge | Add | + +**Action:** Remove adjacency_matrix and unordered/ordered edge traits. Add all bidirectional +query traits and combined query traits. Update `has_contains_edge` to take a second template +parameter `V`. + +--- + +## 4. Function / CPO Discrepancies + +### 4.1 CPO Naming — `edges` → `out_edges` + +| LaTeX Name | Implementation Name | Notes | +| ------------------------ | ---------------------- | ------------------------------------------- | +| `edges(g, u)` | `out_edges(g, u)` | `edges` kept as alias: `auto& edges = out_edges` | +| `edges(g, uid)` | `out_edges(g, uid)` | Same alias pattern | +| `degree(g, u)` | `out_degree(g, u)` | `degree` kept as alias: `auto& degree = out_degree` | +| `find_vertex_edge(g,...)`| `find_out_edge(g,...)` | `find_vertex_edge` kept as alias | +| `contains_edge(g,...)` | `contains_out_edge(g,...)`| `contains_edge` kept as alias | +| `has_edge(g)` | `has_edges(g)` | Renamed (plural) | + +**Action:** Update the function tables to use the new primary names (`out_edges`, `out_degree`, +`find_out_edge`, `contains_out_edge`, `has_edges`). Note that old names are retained as +backward-compatible aliases. + +### 4.2 New CPOs (Not in LaTeX) + +| CPO | Signature(s) | Purpose | +| ---------------------------------------- | ----------------------------------------------------------- | --------------------------- | +| `in_edges(g, u)` / `in_edges(g, uid)` | Returns `in_edge_range_t` | Incoming edges | +| `in_degree(g, u)` / `in_degree(g, uid)` | Returns integral | Incoming edge count | +| `source(g, uv)` | Returns `vertex_t` (via source_id + find_vertex default) | Source vertex descriptor | +| `find_in_edge(g, u, v)` (3 overloads) | Returns in-edge iterator | Find incoming edge | +| `contains_in_edge(g, u, v)` (2 overloads)| Returns bool | Test for incoming edge | + +**Action:** Add these CPOs to the edge function and vertex function tables. + +### 4.3 CPO Resolution Order + +The LaTeX describes functions with "Default Implementation" but doesn't specify the full +CPO resolution order. The implementation uses a 3–4 tier resolution for each CPO: + +1. **Member function** on the graph: `g.fn(args)` +2. **ADL**: `fn(g, args)` found via ADL +3. **Descriptor member**: method on the descriptor itself (where applicable) +4. **Default implementation**: computed from other CPOs + +**Action:** Decide whether to document the full CPO resolution hierarchy or keep the current +approach of showing only the default implementation. At minimum, mention that member and ADL +overrides take priority. + +### 4.4 Edge Function Parameter Changes + +| Aspect | LaTeX (D3130) | Implementation | +| ----------------------------- | ------------------------------ | --------------------------------- | +| `target_id` parameter type | `edge_reference_t uv` | Shared CPO: works on `edge_t` (out-edge descriptor), `in_edge_t`, `edge_list::edge_descriptor`, `edge_data`, or tuple-like types | +| `source_id` parameter type | `edge_reference_t uv` | Same as target_id — multi-tier resolution | +| `edge_value` parameter type | `edge_reference_t uv` | Same — now `edge_value(g, uv)` (2 args, shared CPO) | +| `target` return type | `vertex_t` | Same | +| `source` return type | `vertex_t` | Same (new CPO) | + +Note: `target_id`, `source_id`, and `edge_value` are now **shared CPOs** defined in +`graph/detail/edge_cpo.hpp`, used by both adjacency list and edge list interfaces. They use +a multi-tier resolution strategy: +1. Native edge member (highest priority) +2. ADL with descriptor +3. `adj_list::edge_descriptor` member +4. `edge_list::edge_descriptor` member +5. `edge_data` data member access +6. Tuple/pair access (lowest priority) + +**Action:** Update the edge function table to reflect the unified CPO approach. Replace +`edge_reference_t` parameters with `edge_t` (or appropriate descriptor type). + +### 4.5 Edgelist Function Arity + +| Function | LaTeX (D3130) | Implementation | +| ------------------ | ---------------------- | ------------------------------ | +| `source_id(e)` | 1-arg | `source_id(el, uv)` (2-arg) | +| `target_id(e)` | 1-arg | `target_id(el, uv)` (2-arg) | +| `edge_value(e)` | 1-arg | `edge_value(el, uv)` (2-arg) | +| `contains_edge(el,uid,vid)` | 3-arg | Same | +| `num_edges(el)` | 1-arg | Same | +| `has_edge(el)` | 1-arg | Same | + +The edgelist CPOs now take the edgelist container as the first argument (matching the +adjacency list convention). This is a significant API change. + +**Action:** Update edgelist function table and source files to use 2-arg form for +`source_id`, `target_id`, and `edge_value`. + +--- + +## 5. Edgelist Concept Discrepancies + +| Concept | LaTeX (D3130) | Implementation | +| --------------------------- | ------------------------------------------------ | ----------------------------------------------------------- | +| `sourced_edgelist` | `requires(range_value_t e) { source_id(e); target_id(e); }` | `basic_sourced_edgelist`: `requires(EL& el, range_value_t uv) { source_id(el, uv); target_id(el, uv); }` | +| `sourced_index_edgelist` | `{ source_id(e) } -> integral` | `basic_sourced_index_edgelist`: integral check on 2-arg form | +| `has_edge_value` | `{ edge_value(e) }` | `requires(EL& el, range_value_t uv) { edge_value(el, uv); }` | +| Return type checking | `same_as` on target_id | `convertible_to` on target_id | + +**Action:** Update edgelist concepts to use 2-arg CPO form. Rename `sourced_edgelist` → +`basic_sourced_edgelist`. Change `same_as` to `convertible_to` for target_id check. + +--- + +## 6. Edgelist Type Alias Discrepancies + +| Type Alias (LaTeX) | Implementation Change | +| -------------------------- | ------------------------------------------------------------------------ | +| `edge_value_t` | Now constrained by `has_edge_value` concept, not `basic_sourced_edgelist` | +| `vertex_id_t` | Uses `remove_cvref_t<>` on result of 2-arg `source_id(el, uv)` | +| (new) `raw_vertex_id_t` | Preserves reference-ness of source_id return | +| `edge_reference_t` | Still present in edgelist types — review if needed | + +**Action:** Update edgelist type aliases. Add `raw_vertex_id_t`. Review whether +`edge_reference_t` should be removed from the edgelist as well. + +--- + +## 7. Edgelist Patterns — `edge_info` renamed to `edge_data` + +`edge_info` was **renamed** to `edge_data` (and `vertex_info` to `vertex_data`) to +clarify that these types represent the edge and vertex data models, respectively. + +| Pattern (LaTeX) | Pattern (Implementation) | +| -------------------------------------------- | ------------------------------------------- | +| `edge_info` | `edge_data` | +| `edge_info` | `edge_data` | +| `tuple` | Same | +| `tuple` | Same | +| (new) `pair` | Also supported | +| (new) `edge_data` | With edge descriptor reference | +| (new) `edge_data` | With edge descriptor reference and value | + +**Action:** Rename all `edge_info` → `edge_data` and `vertex_info` → `vertex_data` +throughout the paper (these are renames, not removals — the types still exist under their +new names reflecting their role as data models). Document the full set of supported edge +patterns including `pair` and edge descriptor reference variants. + +--- + +## 8. Data Structures — `vertex_data` and `edge_data` (New) + +These types are defined in `graph_data.hpp` and used by views for structured bindings. +They are **not in the current LaTeX**. + +### 8.1 `vertex_data` + +Template struct with 8 specializations (void combinations of VId, V, VV): +- `{id, vertex, value}` — full +- `{id, vertex}` — no value +- `{id, value}` — no vertex descriptor +- `{id}` — id only +- `{vertex, value}` — no id (descriptor-based) +- `{vertex}` — descriptor only +- `{value}` — value only +- `{}` — empty + +### 8.2 `edge_data` + +Template struct with multiple specializations for sourced/unsourced × void combinations: +- Full: `{source_id, target_id, edge, value}` (sourced) +- `{target_id, edge, value}` (unsourced) +- Various void specializations + +### 8.3 `neighbor_data` + +Similar to `edge_data` but for neighbor views. + +### 8.4 Helper Aliases + +| Alias | Definition | +| ------------------------ | --------------------------------------------- | +| `copyable_vertex_t` | `vertex_data` — `{id, value}` | +| `copyable_edge_t` | `edge_data` — `{source_id, target_id, value}` | +| `copyable_neighbor_t` | `neighbor_data` | + +### 8.5 `graph_error` Exception + +| Aspect | LaTeX | Implementation | +| ---------- | -------------------------------------- | ----------------------------------------- | +| Base class | `runtime_error` | `runtime_error` | +| Constructors | Not specified | `explicit(const string&)`, `explicit(const char*)` | + +**Action:** Add `vertex_data`, `edge_data`, `neighbor_data`, helper aliases, and +`graph_error` to the proposal. These are fundamental to the view iteration model. + +--- + +## 9. Vertex Property Map (New) + +The implementation adds `vertex_property_map` infrastructure in +`adj_list/vertex_property_map.hpp` — not in the LaTeX at all. + +| Component | Purpose | +| --------------------------------------------- | ----------------------------------------------- | +| `vertex_property_map` | Type alias: `vector` for index graphs, `unordered_map` for mapped | +| `make_vertex_property_map(g, init_value)` | Eager factory (all vertices pre-populated) | +| `make_vertex_property_map(g)` | Lazy factory (capacity-reserved) | +| `vertex_property_map_contains(map, uid)` | Test for key presence | +| `vertex_property_map_get(map, uid, default_val)` | Read with default fallback | + +This is critical infrastructure for algorithms on mapped (key-based) graphs, where algorithms +cannot simply index into a `vector` with a vertex ID. + +**Action:** Decide whether to include `vertex_property_map` in this proposal. It is +infrastructure needed for mapped graph algorithm support. If included, add a new subsection. + +--- + +## 10. Value Function Concepts (New) + +The implementation defines `vertex_value_function` and `edge_value_function` concepts in +`graph_concepts.hpp` — not in the LaTeX: + +```cpp +template +concept vertex_value_function = invocable && + (!is_void_v>); + +template +concept edge_value_function = invocable && + (!is_void_v>); +``` + +These are the base concepts that `basic_edge_weight_function` (in algorithms) refines. +They take `(const Graph&, Descriptor)` — matching the 2-arg convention. + +**Action:** Add `vertex_value_function` and `edge_value_function` to the GCI proposal. These +are used by views (vertexlist, incidence, etc.) and algorithms alike. + +--- + +## 11. Namespace Organization + +| Aspect | LaTeX (D3130) | Implementation | +| ------------------- | ------------------------------ | --------------------------------------------------- | +| Adj list namespace | `std::graph` | `graph::adj_list` (CPOs imported into `graph::`) | +| Edgelist namespace | `std::graph::edgelist` | `graph::edge_list` | +| Views namespace | Not specified in D3130 | `graph::views` | +| Container namespace | Not specified | `graph::container` | +| Detail namespace | Not specified | `graph::detail` (shared), `graph::adj_list::detail` | + +**Action:** Update namespace references. Note that `adj_list` CPOs are imported into the +`graph::` namespace via `using` declarations for backward compatibility / convenience. + +--- + +## 12. Multipartite / Partition Support + +The partition support in the LaTeX and implementation are largely aligned: + +| Aspect | LaTeX (D3130) | Implementation | +| ----------------------- | -------------------------------- | ------------------------------------------------- | +| `num_partitions(g)` | Returns integral, default 1 | Same | +| `vertices(g, pid)` | Returns partition vertex range | Same | +| `num_vertices(g, pid)` | Returns integral | Same | +| `partition_id(g, u)` | Returns `partition_id_t` | Same, with descriptor-based default returning 0 | + +No major changes needed here. + +--- + +## 13. Determining vertex_id Type + +| Step | LaTeX (D3130) | Implementation | +| ---- | ----------------------------------------------------------------- | ----------------------------------------------------- | +| 1 | Use vertex_id(g,u) override | Same (member → ADL → default) | +| 2 | Pattern: `random_access_range>` | Subsumed by descriptor pattern detection | +| 3 | Default: `size_t` | Default: `size_t` (for random-access); key type for maps | + +The implementation determines vertex_id type through the descriptor: +- For `random_access_iterator`: `storage_type = size_t` +- For `keyed_vertex_type` (maps): key type from the pair-like iterator + +**Action:** Update the "Determining the vertex_id and its type" section to reflect +the descriptor-based approach. The pattern-matching description is obsolete. + +--- + +## 14. Missing Information / Open Questions + +### Q1 — Descriptor Exposition Level +**Question:** How much internal descriptor implementation detail should the proposal expose? +The current implementation has rich internals (storage_type, underlying_value, inner_value, +pattern detection concepts). The proposal needs to define the *observable interface* without +over-specifying implementation details. + +**Options:** +- (a) Show full descriptor class specifications (as-is from implementation) +- (b) Show only the interface users interact with (vertex_id, inner_value, comparison, + increment) and mark internals as exposition-only +- (c) Define descriptors purely through their concepts and observable behavior + +**Decision: Hybrid (b)+(c), following WG21 convention.** + +Standards proposals specify *what*, not *how*. The approach mirrors how the standard handles +`std::optional` (exposition-only `*val*`), `std::expected`, and range adaptors: + +1. **Concepts are normative.** `vertex_descriptor_type`, `edge_descriptor_type`, + `random_access_descriptor`, `iterator_based_descriptor` — fully specified as the primary + contract. This is how `iterator`, `range`, `allocator` are specified in the standard. + +2. **Descriptor classes are the default implementation.** `vertex_descriptor` and + `edge_descriptor<...>` are specified with exposition-only data members (italic names per + WG21 convention: *`storage-type`*, *`underlying-value`*) and a public observable interface + (construction, `vertex_id()`, comparison, increment). Implementations may use different + internal representations as long as observable behavior matches. + +3. **Pattern detection concepts are fully specified.** `simple_edge_type`, `pair_edge_type`, + `tuple_edge_type`, etc. define how the library auto-detects user data structures — these + are part of the customization-point machinery and user code depends on them. + +4. **Descriptor traits are fully specified.** `is_vertex_descriptor_v`, + `is_edge_descriptor_v`, etc. are used in SFINAE/concepts by user code and must be normative. + +### Q2 — Descriptor Passing Convention and `vertex_reference_t` / `edge_reference_t` Replacement + +**Question:** The paper text (not just the code) uses `vertex_reference_t` and +`edge_reference_t` in explanatory prose and concept definitions. All such references +must be replaced with `vertex_t` and `edge_t` respectively — the descriptor types +that supersede them. Should descriptors be passed by value or by `const&`? + +**Decision: Pass descriptors by value in function/CPO signatures; use `const&` only in +concept `requires` clauses.** + +Size analysis of the implementation's descriptors: + +| Descriptor | Members | Size (all configurations) | Trivially copyable | +|---|---|---|---| +| `vertex_descriptor` | 1 field (`size_t` or iterator) | **8 bytes** | Yes | +| `edge_descriptor` | 2 fields (edge position + source vertex) | **16 bytes** | Yes | + +`EdgeDirection` is a stateless tag — zero instance overhead. Even for iterator-based +containers (maps, lists), standard library iterators are a single pointer (8 bytes), so +`edge_descriptor` is always exactly 16 bytes. + +**Rationale for pass-by-value:** + +1. **16 bytes is the pass-by-value sweet spot.** `string_view` and `span` (both 16 bytes) + are passed by value in the standard. Edge descriptors are the same size. +2. **Register-passed on all major ABIs.** Both descriptors fit in registers: + + | ABI | Register-passed limit | vertex (8B) | edge (16B) | + |-----------------|----------------------|-------------|-----------------------------| + | System V x86-64 | 16 bytes in 2 regs | 1 register | 2 registers | + | ARM64 (AAPCS64) | 16 bytes in 2 regs | 1 register | 2 registers | + | MSVC x64 | 8 bytes in 1 reg | 1 register | Indirect (pointer), but optimized away for inlined CPOs | + + ARM64 has a hard 16-byte cutoff — at 17 bytes the ABI switches to indirect passing. + Our descriptors sit exactly at or below the limit. MSVC x64 technically passes >8-byte + types by pointer, but graph CPOs are `constexpr` function objects that inline, so the + optimizer eliminates the indirection. `string_view` (16 bytes) uses the same by-value + convention on MSVC. +3. **No aliasing concerns.** Pass-by-value eliminates the possibility of the descriptor + aliasing the graph's internal storage — the compiler can optimize more aggressively. +4. **Matches the iterator convention.** The standard passes iterators by value universally + (`std::sort(It first, It last)`). Descriptors are the moral equivalent of iterators. + +**Convention distinction:** +- **Concept requirements:** `requires(G& g, const E& e) { target_id(g, e); }` — uses + `const&` (standard practice for expressing requirements on types) +- **Function/CPO signatures in the paper:** `target_id(G& g, edge_t e)` — pass by value +- **User code:** Pass by value — `auto eid = target_id(g, e);` + +There is no case where `const&` is needed for the standard-provided descriptors. The +descriptor concepts should imply efficient copyability (like iterator requirements). + +**Action needed:** Full text search of `container_interface.tex` for `vertex_reference`, +`edge_reference`, `vertex_info`, `edge_info` in prose text. Replace every occurrence of +`vertex_reference_t` with `vertex_t`, `edge_reference_t` with `edge_t`, +`vertex_info` with `vertex_data`, and `edge_info` with `edge_data` (the latter two are +renames reflecting the data model role). Ensure all function signatures pass descriptors +by value, not by reference. + +**Full audit results** (completed): + +**`container_interface.tex`** — 9 occurrences: + +| Line | Context | Action | +|---|---|---| +| 157 | Type alias table row: `vertex_reference_t` | **Remove row** | +| 164 | Type alias table row: `edge_reference_t` | **Remove row** | +| 468 | Edgelist type alias table row: `edge_reference_t` | **Remove or keep per Q3** | +| 498 | `contains_edge` default impl lambda: `[](edge_reference_t e)` | **Replace with `edge_t e`** | +| 511 | LaTeX comment: `[](edge_reference_t)` | **Update or remove** | +| 514 | Prose: "based on the `tuple` and `edge_info` types" | **Rename `edge_info` → `edge_data`** | +| 523 | Prose: "The `edge_info` patterns are" | **Rename `edge_info` → `edge_data`** | +| 525 | Pattern listing: `edge_info` | **Rename `edge_info` → `edge_data`** | +| 526 | Pattern listing: `edge_info` | **Rename `edge_info` → `edge_data`** | + +**`D3130_Container_Interface/src/`** — 8 occurrences: + +| File | Line | Context | Action | +|---|---|---|---| +| `concepts_edges.hpp:4` | `edge_reference_t uv` in `targeted_edge` | **File will be rewritten** (Phase 2, task 5) | +| `concepts_edges.hpp:10` | `edge_reference_t uv` in `sourced_edge` | **File will be rewritten** | +| `concepts_edges_before.hpp:4` | `edge_reference_t uv` in `basic_targeted_edge` | **File will be removed** (Phase 2, task 9) | +| `concepts_edges_before.hpp:7` | `edge_reference_t uv` in `basic_sourced_edge` | **File will be removed** | +| `concepts_edges_before.hpp:14` | `edge_reference_t uv` in `targeted_edge` | **File will be removed** | +| `concepts_edges_before.hpp:18` | `edge_reference_t uv` in `sourced_edge` | **File will be removed** | +| `concepts_target_edge_range.hpp:6` | `vertex_reference_t u` | **File will be rewritten** (Phase 2, task 6) | +| `edgelist_types.hpp:11` | `using edge_reference_t = ...` | **Remove or keep per Q3** | + +**Note:** 0 occurrences of `vertex_info` in any D3130 file. `edge_info` appears 4 times (all in prose, lines 514–526). + +### Q3 — Unified Edge Descriptor Story (Edgelist + Adjacency List) + +**Original question:** Should the edgelist keep `edge_reference_t` or align with the +adjacency list pattern? + +**Decision: Remove `edge_reference_t`. Unify the descriptor story across both interfaces +so algorithms can work interchangeably with adjacency list edges and edge list edges.** + +#### Background + +The implementation has **two separate edge descriptor types** with incompatible designs: + +| Aspect | `adj_list::edge_descriptor` | `edge_list::edge_descriptor` | +|---|---|---| +| Storage | Values (`size_t` indices or iterators) | References (`const VId&` + `reference_wrapper`) | +| Trivially copyable | Yes (RA case) | No (reference members) | +| Assignable | Yes | No (copy/move assignment deleted) | +| Needs graph for IDs | Yes (for `target_id`) | No (self-contained) | +| `source()`/`target()` (vertex descriptors) | Yes (`source()`) | No | +| Trait | `is_edge_descriptor_v` | `is_edge_list_descriptor_v` | +| sizeof (typical) | 16 bytes | 16 bytes (no EV), 24 bytes (with EV) | + +The `edge` concept gates on `is_edge_descriptor_v` and requires `source(g,e)` / +`target(g,e)` — both exclude edge list edges entirely. An algorithm written against +`edge` silently rejects edge list edges. + +#### Barriers to unification + +1. **Type trait gate** — `edge` checks `is_edge_descriptor_v`, which only matches + `adj_list::edge_descriptor`. Edge list descriptors have a separate trait. + +2. **`source(g,e)` / `target(g,e)` require a vertex container** — These return vertex + descriptors. Edge lists have no vertex container to index into; they only know IDs. + +3. **Reference semantics** — `edge_list::edge_descriptor` stores `const VId&` references, + making it non-assignable and dangling-prone. Algorithms that store edges (e.g., priority + queue for Kruskal's) would fail. + +4. **Edge value in descriptor** — `edge_list::edge_descriptor` carries the value + directly. With a value-based redesign, `descriptor` = 24 bytes, exceeding + the 16-byte register-pair threshold. + +5. **Edge value access asymmetry** — `adj_list` needs the graph to look up edge values; + `edge_list` carries them directly. The shared CPOs handle dispatch, but semantics differ. + +#### Recommended layered concept design + +Define a lighter common concept both can satisfy, with adj_list refining it: + +``` +graph_edge ← common: source_id(g,e) + target_id(g,e) + └── adj_list::edge ← refines: + is_edge_descriptor_v + source(g,e) + target(g,e) +``` + +Most algorithms only need vertex IDs (BFS, DFS, Dijkstra, Bellman-Ford, topological sort, +Kruskal's). Only algorithms that need to *navigate back to vertex data* require the full +`edge` with `source(g,e)` / `target(g,e)`. + +#### Paper changes required + +| # | Change | Section | +|---|---|---| +| P1 | Remove `edge_reference_t` from edgelist type alias table and all usages | §5, §6 tables | +| P2 | Remove `edge_reference_t` from `edgelist_types.hpp` source file | §15 source files | +| P3 | Replace `[](edge_reference_t e)` lambda in `contains_edge` with `[](edge_t e)` | §4.5 function table | +| P4 | Define `graph_edge` common concept: `requires(G& g, const E& e) { source_id(g, e) → convertible_to>; target_id(g, e) → convertible_to>; }` | New in §1 | +| P5 | Refine `edge` to explicitly extend `graph_edge` | §1.1 | +| P6 | Document the unified descriptor story — explain that `graph_edge` is the interop concept, `edge` is for adjacency-list-specific vertex navigation | §0 or new section | +| P7 | Document that edge list descriptors should be value types matching the pass-by-value convention | §0.2 / new edgelist descriptor section | +| P8 | Specify that `edge_list::edge_descriptor` stores only IDs (not the edge value), with value accessed via `edge_value(el, e)` against the container — keeping sizeof ≤ 16 bytes | Edgelist section | +| P9 | Add `is_edge_list_descriptor_v` trait to the traits table, or unify with `is_edge_descriptor_v` under a common `is_graph_edge_descriptor_v` | §3 traits | + +#### Implementation changes required + +| # | Change | File(s) | +|---|---|---| +| I1 | Redesign `edge_list::edge_descriptor` as a **value type**: store `VId source_id_; VId target_id_;` (values, not references). Drop `EV` template parameter — value accessed via `edge_value(el, e)` | `edge_list/edge_list_descriptor.hpp` | +| I2 | Make `edge_list::edge_descriptor` fully copyable, assignable, and trivially copyable (for integral VId). sizeof = `2 * sizeof(VId)` = 16 bytes for `size_t` | `edge_list/edge_list_descriptor.hpp` | +| I3 | Add `graph_edge` concept (or whatever name is chosen) to a shared location | New file or `graph_concepts.hpp` | +| I4 | Update `edge` to refine `graph_edge` | `adj_list/adjacency_list_concepts.hpp` | +| I5 | Add `edge_list::edge_descriptor` support to `is_graph_edge_descriptor_v` (if unified trait chosen) or create `is_edge_list_descriptor_v` → `graph_edge`-satisfying trait | `edge_list/edge_list_traits.hpp` | +| I6 | Update `edge_cpo.hpp` shared CPO tiers to handle the value-based edge_list descriptor | `detail/edge_cpo.hpp` | +| I7 | Update `edge_value` CPO: for edge_list descriptors that no longer carry the value, add a tier that indexes back into the edge list container | `detail/edge_cpo.hpp` | +| I8 | Create an `edge_list::edge_descriptor_view` (optional) that wraps a plain edge range to produce `edge_list::edge_descriptor` objects, analogous to `adj_list::edge_descriptor_view` | New `edge_list/edge_list_descriptor_view.hpp` | +| I9 | Update edge_list concepts to work with the redesigned descriptor | `edge_list/edge_list.hpp` | + +#### Open sub-questions + +- **Naming: DECIDED — relax `edge` by dropping the trait gate.** This makes `edge` + the general concept that works for both adjacency list and edge list edges. It becomes: + `requires(G& g, const E& e) { source_id(g, e); target_id(g, e); }` — no + `is_edge_descriptor_v` check. Any type that supports `source_id`/`target_id` via the CPOs + qualifies, including plain tuples, `edge_data`, `edge_list::edge_descriptor`, and + `adj_list::edge_descriptor`. Algorithms that additionally need vertex descriptors + (`source(g,e)` / `target(g,e)`) add those requirements directly rather than relying on a + trait gate. This is more general and avoids the need for a separate `graph_edge` concept. + **Paper change P4 is superseded** — instead of adding `graph_edge`, relax `edge`. + **Implementation change I3 is superseded** — modify `edge` in place rather than + adding a new concept. +- **Edge value access: DECIDED — descriptor stores only an iterator (always, never an index).** + Unlike `adj_list::edge_descriptor` which uses `conditional_t`, the + `edge_list::edge_descriptor` **always** stores an iterator. The key insight is that edge + list elements already carry both source and target IDs — there is nothing to cache. So the + descriptor is just the iterator: + + ```cpp + template + struct edge_descriptor { + EdgeIter position_; // 8 bytes — that's it + }; + ``` + + - `source_id(el, e)` → extracts from `*e.position_` (pattern dispatch: tuple get, member, etc.) + - `target_id(el, e)` → extracts from `*e.position_` + - `edge_value(el, e)` → extracts from `*e.position_` + + **Why always-iterator, not conditional like adj_list:** + - `adj_list` caches index for RA containers because (1) vertex_id = index optimization, + (2) index stability across graph mutation, (3) long-lived navigation handles stored in + predecessor maps and priority queues. **None of these apply to edge lists.** + - Edge lists are consumed immutably — descriptors are transient iteration handles that live + only for the current loop iteration, never stored across container mutations. + - Always-iterator = **1 field, 8 bytes, 1 code path** vs 3 fields, 24 bytes, 2 code paths. + + **User impact: None.** Users interact exclusively through CPOs (`source_id`, `target_id`, + `edge_value`). They never construct descriptors (views produce them), never inspect internal + storage, and never see the sizeof. Code is identical for both adj_list and edge_list edges: + ```cpp + template requires edge + void process(G& g, E e) { auto sid = source_id(g, e); } // works for both + ``` + + **The one theoretical edge case:** Storing an edge_list descriptor across a + `vector::push_back` on the edge list would dangle (iterator invalidation). But this is the + same behavior adj_list has for non-RA edge containers, and it's a misuse pattern — you + shouldn't store iteration-produced descriptors across mutations. + + **Implementation change I1 revised:** `edge_list::edge_descriptor` stores only + `EdgeIter position_` (8 bytes, trivially copyable, fully assignable). No `VId` members. + **Implementation change I7 revised:** All edge_list CPO tiers dereference the iterator to + extract IDs and values — no stored position indexing needed. +- **`edge_list::edge_descriptor_view`: DECIDED — No view. `edges(el)` returns the raw range.** + A wrapping view would add complexity, a lifetime dependency, and break structured bindings + with zero user-visible benefit through the CPO interface. + + **Why no view is needed (unlike adj_list):** + - `adj_list::edge_descriptor_view` exists because per-vertex edges don't know their source + vertex — the view injects that context. Edge list elements already carry both endpoints. + There is nothing to wrap. + - Since we dropped the trait gate on `edge`, the concept is just `source_id(g,e)` + + `target_id(g,e)`. Plain tuples, pairs, and `edge_data` already satisfy this through the + CPO's lower-priority tiers (tuple_like, pair, data member). No descriptor wrapping needed. + + **What you lose with a view (flexibility cost):** + 1. **Self-contained values** — Raw elements can be copied, sorted, stored in priority queues + freely (e.g., Kruskal's sorts edges by weight). A descriptor would create iterator + lifetime dependencies. + 2. **Structured bindings** — `auto [src, tgt, val] = e;` works out of the box with tuples + and pairs. A descriptor view would break this unless tuple protocol is added. + 3. **User data passthrough** — Custom edge structs with extra fields are directly accessible. + A descriptor erases the element type behind an iterator. + + **User-visible behavior:** Identical through CPOs either way. The only difference is + `edge_t` is the user's raw element type (tuple, pair, `edge_data`, custom struct) + rather than a descriptor — which is actually **better** for edge lists since users expect + to work with their own data types directly. + + **Implementation changes I1, I8 superseded:** No `edge_list::edge_descriptor` class or + `edge_list::edge_descriptor_view` needed. Edge list edges are the raw range elements. + CPO dispatch handles all supported patterns (tuple, pair, `edge_data`, custom structs) + directly. + +### Q4 — Adjacency Matrix Support +**Question:** The LaTeX includes `define_adjacency_matrix`, `is_adjacency_matrix`, +and `adjacency_matrix` traits/concepts, and `contains_edge` has a special constant-time +implementation for adjacency matrices. These are **all removed** from the implementation. +Should they remain in the proposal as planned/future, or be dropped entirely? + +**Decision: Keep the adjacency matrix traits/concepts in the paper.** + +When an adjacency matrix is used, the traits enable algorithms to assume constant-time +random access for `contains_out_edge(g, u, v)` and `contains_out_edge(g, uid, vid)`. +Without the trait, algorithms must conservatively assume linear-time edge lookup, even when +the underlying container supports O(1) access. + +**What to include:** +- `define_adjacency_matrix` — user-specializable trait to declare a graph is an adjacency matrix +- `is_adjacency_matrix` / `is_adjacency_matrix_v` — query trait +- `adjacency_matrix` — concept combining the trait with `index_adjacency_list` +- `contains_out_edge` constant-time guarantee when `adjacency_matrix` is satisfied + +**Note:** These are not yet in the implementation. The paper should include them with a note +that the reference implementation has not yet added adjacency matrix support, but the +traits/concepts are designed to be ready when it does. The implementation will need to add +these back. + +### Q5 — `raw_vertex_id_t` Inclusion +**Question:** The implementation defines `raw_vertex_id_t` which preserves reference +qualifiers on the vertex ID (important for map-based graphs where vertex IDs are const +references to keys). Should this be in the proposal? + +**Decision: Make `raw_vertex_id_t` exposition-only; make `vertex_id_store_t` normative.** + +`raw_vertex_id_t` is unusual for a standard library — it's a "raw decltype" alias +(preserves references) used only as a metaprogramming stepping stone. The standard doesn't +expose such aliases elsewhere. However, the optimization it enables is genuinely important. + +**How it works:** +- `vertex_id_t` = `remove_cvref_t` — always a clean value type +- `raw_vertex_id_t` = `decltype(vertex_id(g, u))` — preserves `const string&` for maps + +**Its sole consumer is `vertex_id_store_t`** (in `traversal_common.hpp`): +```cpp +using vertex_id_store_t = conditional_t< + is_reference_v>, + reference_wrapper>>, + vertex_id_t>; +``` +This avoids copying expensive vertex IDs (e.g., `string` map keys) into algorithm data +structures by storing a `reference_wrapper` instead. For integral IDs (`size_t`) it's a +no-op — `vertex_id_store_t` = `vertex_id_t`. + +**Options considered:** + +| Option | Approach | Verdict | +|---|---|---| +| A — Include both | Exposes unusual "raw decltype" alias | Too unusual for standard | +| **B — exposition-only `raw_vertex_id_t`, normative `vertex_id_store_t`** | User gets the optimization; internal metaprogramming hidden | **Selected** | +| C — Drop both, mandate cheap IDs | Simplest but excludes `map` graphs | Too restrictive | +| D — Drop both, rely on `auto` | Works but loses named type for docs/concepts | Insufficient for specification | + +**In the paper:** +- *`raw-vertex-id-type`* (italic, exposition-only per WG21 convention) +- `vertex_id_store_t` — normative public alias, documented as "the type algorithms use to + store vertex IDs efficiently" +- Matches precedent: `std::optional` has exposition-only *`val`* but public `value()` + +### Q6 — Edgelist Namespace Merge +**Question:** The LaTeX has a `\phil` note about wanting to merge edgelist into `std::graph` +but having difficulty. The implementation uses `graph::edge_list`. Should the proposal keep +separate namespaces or attempt to merge? + +**Decision: Keep separate namespaces. Use `std::graph::edge_list`.** + +Rationale: +1. **Type alias collisions.** `vertex_id_t` (adj_list) and `vertex_id_t` (edge_list) + have different definitions. Template aliases can't be disambiguated by concepts, so they + must live in separate namespaces. +2. **Different abstractions.** Adjacency lists organize edges per-vertex (graph structure). + Edge lists are flat ranges (data interchange). Separate namespaces make this explicit. +3. **Shared CPOs work already.** `source_id`, `target_id`, `edge_value` live in `graph::` + and dispatch to the right tier via type traits — cross-namespace sharing is solved. +4. **Matches the implementation.** `graph::adj_list` and `graph::edge_list` work well in + practice. The `\phil` note was written before the collision problem was understood. + +**Paper action:** Update `std::graph::edgelist` → `std::graph::edge_list`. Remove the +`\phil` note on line 417 of `container_interface.tex`. + +### Q7 — `vertex_property_map` Scope +**Question:** Is `vertex_property_map` part of the GCI (D3130) or should it be in the +Algorithms paper (D3128) or a separate utility paper? It is used by algorithms but defined +alongside the graph interface. + +**Decision: Include `vertex_property_map` in the GCI (D3130).** + +Rationale: +1. **Defined by container properties.** `vertex_property_map` resolves to `vector` + for index graphs or `unordered_map, T>` for mapped graphs. That decision + depends entirely on GCI concepts (`index_vertex_range` vs `mapped_vertex_range`). +2. **Algorithms consume, not define.** BFS/DFS/Dijkstra *use* property maps for colors, + distances, predecessors — but the map's type and construction depend on the container, not + the algorithm. Putting it in D3128 would create a circular dependency. +3. **Parallels `vertex_id_store_t`** (Q5) — both are type aliases that adapt to the + graph's container strategy. Both belong in the GCI. +4. **Custom containers need it.** Users implementing new graph containers satisfying GCI + concepts should get `vertex_property_map` working automatically — only possible if it's + defined alongside the concepts it depends on. + +### Q8 — Source CPO Default Behavior +**Question:** The LaTeX marks `source_id(g, uv)` as "n/a, optional". In the implementation, +`source_id` always works on edge descriptors (because `edge_descriptor` stores the source +vertex). Is `source_id` still "optional" or has it become mandatory for any edge_descriptor? + +**Decision: `source_id` is effectively mandatory.** + +Every `edge_descriptor` stores the source vertex, so `source_id(g, uv)` resolves +automatically for any graph whose edges are descriptors. The paper should drop the +"n/a, optional" qualifier and list `source_id` alongside `target_id` as a supported CPO for +descriptor-based edges. For custom edge types that do not use descriptors, the user must still +provide an override — but that is a general CPO customisation rule, not a special case. + +--- + +## 15. Source File Reorganization + +The LaTeX includes source files from `D3130_Container_Interface/src/` via `\lstinputlisting`. +These need to be rewritten: + +| LaTeX Source File | Status | Action | +| ------------------------------------- | ----------- | ------------------------------------------------ | +| `concepts_edges.hpp` | Obsolete | Replace with single `edge` concept | +| `concepts_edges_before.hpp` | Obsolete | Remove (basic variants eliminated) | +| `concepts_target_edge_range.hpp` | Obsolete | Replace with `out_edge_range` | +| `concepts_vertex_range.hpp` | Outdated | Rewrite: add `vertex`, update `index_vertex_range`, add `mapped_vertex_range` | +| `concepts_adj_list.hpp` | Outdated | Rewrite: remove sourced/basic variants, add bidirectional/mapped | +| `concepts_basic_adj_list.hpp` | Obsolete | Remove entirely | +| `descriptor.hpp` | Obsolete | Replace with split descriptors (vertex + edge) | +| `descriptor_view.hpp` | Outdated | Replace with vertex_descriptor_view + edge_descriptor_view | +| `edgelist_concepts.hpp` | Outdated | Update to 2-arg CPO form, rename concepts | +| `edgelist_types.hpp` | Outdated | Update to 2-arg form, add `raw_vertex_id_t` | +| `edgelist_typetraits.hpp` | Minimal | Same | +| (new) `descriptor_traits.hpp` | Missing | Add descriptor trait types and concepts | +| (new) `value_concepts.hpp` | Missing | Add `vertex_value_function`, `edge_value_function` | + +--- + +## 16. Prioritized Task List + +### Phase 0: Decision Summary (All Resolved — No File Edits) + +All eight open questions are resolved. This phase is a reference checklist only. + +- **Q1 — Descriptor exposition:** Hybrid (b)+(c). Concepts normative, descriptor classes + with exposition-only internals, pattern-detection concepts and traits fully specified. +- **Q2 — Passing convention:** Pass descriptors by value. Full prose audit completed + (17 occurrences catalogued in Q2). Execute replacements in Phases 3–4. +- **Q3 — Unified edge story:** Drop `is_edge_descriptor_v` trait gate on `edge`. + No `edge_list::edge_descriptor` class. No `edge_list::edge_descriptor_view`. Raw edge list + elements satisfy `edge` directly through CPO tier dispatch. +- **Q4 — Adjacency matrix:** Keep traits/concepts in paper (`define_adjacency_matrix`, + `is_adjacency_matrix`, `adjacency_matrix`, `contains_out_edge` O(1) guarantee). + Note reference implementation has not yet added support. +- **Q5 — `raw_vertex_id_t`:** Exposition-only (*`raw-vertex-id-type`*). + `vertex_id_store_t` normative. +- **Q6 — Namespace:** Separate namespaces — `std::graph::edge_list`. Remove `\phil` merge + note. +- **Q7 — `vertex_property_map`:** Include in D3130 (GCI). Defined by container properties, + parallels `vertex_id_store_t`. +- **Q8 — `source_id` optionality:** Effectively mandatory for descriptor-based edges. Drop + "n/a, optional" qualifier. Custom non-descriptor edge types still need user override. + +### Phase 1: Descriptor Architecture (Foundation — Must Be First) + +1. **Rewrite descriptor.hpp** — Replace single `descriptor` with split + `vertex_descriptor` + `edge_descriptor`. + Use exposition-only data members per Q1. +2. **Rewrite descriptor_view.hpp** — Replace `descriptor_view` / `descriptor_subrange_view` + with `vertex_descriptor_view` + `edge_descriptor_view`. +3. **Add descriptor_traits.hpp** — Type traits and concepts for descriptor detection. +4. **Update descriptor prose** — Rewrite "Vertex and Edge Descriptor Views" section. Include + "Why Descriptors?" rationale (§0.2a). Explain pass-by-value convention per Q2. + +### Phase 2: Concept Overhaul + +5. **Rewrite `edge`** — Replace 6 edge concepts with single relaxed `edge`: + `requires(G& g, const E& e) { source_id(g, e); target_id(g, e); }`. No + `is_edge_descriptor_v` trait gate (Q3). Both adj_list descriptors and raw edge list + elements satisfy this concept. +6. **Rewrite edge range concepts** — Replace `targeted_edge_range` / + `basic_targeted_edge_range` with `out_edge_range` + `in_edge_range`. +7. **Rewrite vertex concepts** — Add `vertex`, update `vertex_range`, + `index_vertex_range`, add `mapped_vertex_range`. +8. **Rewrite adjacency list concepts** — Remove all `basic_*` / `sourced_*` variants, add + `bidirectional_adjacency_list`, `mapped_adjacency_list`, etc. +9. **Remove obsolete concept source files** — `concepts_edges_before.hpp`, + `concepts_basic_adj_list.hpp`. + +### Phase 3: Type Aliases and Traits + +10. **Update type alias table** — Replace `vertex_reference_t` → `vertex_t` (descriptor), + `edge_reference_t` → `edge_t` (descriptor); remove `graph_reference_t`; add + `out_edge_range_t`, `out_edge_iterator_t`, `out_edge_t`, `in_edge_range_t`, + `in_edge_iterator_t`, `in_edge_t`. +11. **Update vertex ID aliases** — Add `vertex_id_store_t` as normative. Add + *`raw-vertex-id-type`* as exposition-only (Q5). +12. **Update traits table** — Remove `unordered_edge` / `ordered_edge` traits. Keep + adjacency matrix traits (Q4): `define_adjacency_matrix`, `is_adjacency_matrix` / + `_v`, `adjacency_matrix`. Add bidirectional query traits, combined query traits. +13. **Update `has_contains_edge`** — Add second template parameter V. + +### Phase 4: Function Tables and CPOs + +14. **Rename primary CPOs** — `edges` → `out_edges` (with alias notes), `degree` → + `out_degree`, `find_vertex_edge` → `find_out_edge`, `contains_edge` → + `contains_out_edge`, `has_edge` → `has_edges`. +15. **Add incoming-edge CPOs** — `in_edges`, `in_degree`, `find_in_edge`, + `contains_in_edge`. +16. **Update `source_id` / `source` CPOs** — `source_id(g, uv)` is mandatory for + descriptor-based edges (Q8). Drop "n/a, optional" qualifier. Add `source(g, uv)` CPO + returning vertex descriptor. +17. **Execute Q2 audit replacements** — Replace `edge_reference_t` → `edge_t` and + `vertex_reference_t` → `vertex_t` in all function parameters, concept definitions, + and signatures (17 occurrences catalogued in Q2). Pass descriptors by value. + +### Phase 5: Edgelist Updates + +18. **Update edgelist concepts** — Switch to 2-arg CPO form. Edge list edges are raw range + elements (tuples, pairs, `edge_data`, custom structs) — no `edge_list::edge_descriptor` + class or view needed (Q3). Edges satisfy `edge` via CPO tier dispatch. +19. **Remove `edge_reference_t`** — From edgelist type alias table and + `edgelist_types.hpp` source file. Replace `[](edge_reference_t e)` lambda in + `contains_edge` with the raw element type. +20. **Update edgelist type aliases** — 2-arg form, add `raw_vertex_id_t`. +21. **Update edgelist function table** — 2-arg for `source_id`, `target_id`, `edge_value`. +22. **Rename edgelist patterns** — `edge_info` → `edge_data`, `vertex_info` → `vertex_data` + (renamed to reflect data model role). Add supported patterns. +23. **Update namespace** — `std::graph::edgelist` → `std::graph::edge_list` (Q6). Remove + `\phil` merge note (line 417). + +### Phase 6: New Sections + +24. **Add `vertex_data` / `edge_data` / `neighbor_data`** — New classes and structs section. +25. **Add value function concepts** — `vertex_value_function`, `edge_value_function`. +26. **Add `vertex_property_map`** — New GCI subsection (Q7). Show resolution to `vector` + for index graphs and `unordered_map, T>` for mapped graphs. +27. **Update vertex_id determination** — Descriptor-based approach. + +### Phase 7: Prose and Cleanup + +28. **Audit all prose** — Search-and-replace: `vertex_reference_t` → `vertex_t`, + `edge_reference_t` → `edge_t`, `vertex_info` → `vertex_data`, `edge_info` → `edge_data`, + `targeted_edge` → `edge`, `sourced_edge` → (remove), `basic_*` → (remove) in + explanatory text. +29. **Update adjacency matrix prose** — Revise `contains_out_edge` discussion to match Q4 + decision (keep traits/concepts, note future implementation). Remove stale traits + discussion that assumed removal. +30. **Review "Using Existing Data Structures"** section for accuracy. +31. **Update revision history**. +32. **Cross-reference with D3128 (Algorithms), D3129 (Views), D3127 (Terminology)** — Ensure + consistent type names and concept names across all papers. + +--- + +## 17. Summary of Removed Items + +| Item | Was in LaTeX as | +| ------------------------------ | -------------------------------------------- | +| `vertex_reference_t` | Type alias — **replaced by `vertex_t`** (descriptor) | +| `edge_reference_t` | Type alias, used in concept parameters — **replaced by `edge_t`** (descriptor) | +| `graph_reference_t` | Type alias | +| `vertex_info` | **Renamed to `vertex_data`** — represents the vertex data model | +| `edge_info` | **Renamed to `edge_data`** — represents the edge data model | +| `targeted_edge` | Concept | +| `sourced_edge` | Concept | +| `sourced_targeted_edge` | Concept | +| `basic_targeted_edge` | Concept | +| `basic_sourced_edge` | Concept | +| `basic_sourced_targeted_edge` | Concept | +| `targeted_edge_range` | Concept | +| `basic_targeted_edge_range` | Concept | +| `_common_vertex_range` | Concept | +| `basic_adjacency_list` | Concept | +| `basic_index_adjacency_list` | Concept | +| `basic_sourced_adjacency_list` | Concept | +| `basic_sourced_index_adjacency_list` | Concept | +| `sourced_adjacency_list` | Concept | +| `sourced_index_adjacency_list` | Concept | +| `define_unordered_edge` | Trait | +| `unordered_edge` | Concept/trait | +| `ordered_edge` | Concept/trait | +| `descriptor` | Single monolithic descriptor class | +| `descriptor_view(R&&)` | Factory function | +| `descriptor_subrange_view(R&&, R&&)` | Factory function | + +## 18. Summary of New Items + +| Item | Category | +| ---------------------------------------- | ---------------- | +| `vertex_descriptor` | Class | +| `edge_descriptor` | Class | +| `vertex_descriptor_view` | View class | +| `edge_descriptor_view` | View class | +| `out_edge_tag` / `in_edge_tag` | Tag types | +| `edge` / `vertex` | Concepts | +| `out_edge_range` / `in_edge_range` | Concepts | +| `ordered_vertex_edges` | Concept | +| `bidirectional_adjacency_list` | Concept | +| `index_bidirectional_adjacency_list` | Concept | +| `hashable_vertex_id` | Concept | +| `mapped_vertex_range` | Concept | +| `mapped_adjacency_list` | Concept | +| `mapped_bidirectional_adjacency_list` | Concept | +| `vertex_descriptor_type` / `edge_descriptor_type` | Concepts | +| `descriptor_type` / `random_access_descriptor` / `iterator_based_descriptor` | Concepts | +| All descriptor traits (`is_vertex_descriptor`, etc.) | Type traits | +| Edge/vertex pattern detection concepts | Concepts | +| `out_edge_range_t`, `out_edge_iterator_t`, `out_edge_t` | Type aliases | +| `in_edge_range_t`, `in_edge_iterator_t`, `in_edge_t` | Type aliases | +| *`raw-vertex-id-type`* (exposition-only) | Type alias (Q5) | +| `vertex_id_store_t` | Type alias (Q5) | +| `in_edges(g, u)` / `in_edges(g, uid)` | CPO | +| `in_degree(g, u)` / `in_degree(g, uid)` | CPO | +| `source(g, uv)` | CPO | +| `find_in_edge(g, ...)` (3 overloads) | CPO | +| `contains_in_edge(g, ...)` (2 overloads)| CPO | +| `has_in_degree` / `_v` | Trait | +| `has_find_in_edge` / `_v` | Trait | +| `has_contains_in_edge` / `_v` | Trait | +| `has_basic_queries` / `_v` | Trait | +| `has_full_queries` / `_v` | Trait | +| `vertex_data` | Struct (8 specs) | +| `edge_data` | Struct (16 specs)| +| `neighbor_data` | Struct | +| `copyable_vertex_t` / `copyable_edge_t` / `copyable_neighbor_t` | Aliases | +| `vertex_value_function` | Concept | +| `edge_value_function` | Concept | +| `vertex_property_map` | Type alias (Q7) | +| `graph_error` | Exception class | +| `vertex_property_map` | Type alias | +| `make_vertex_property_map(g, ...)` | Factory functions | +| `vertex_property_map_contains(m, uid)` | Utility function | +| `vertex_property_map_get(m, uid, dflt)` | Utility function | diff --git a/tex/conventions.tex b/tex/conventions.tex index 2607e5b..61c2d84 100644 --- a/tex/conventions.tex +++ b/tex/conventions.tex @@ -17,8 +17,7 @@ \section{Naming Conventions} \hline \tcode{EL} & & \tcode{el} & Edge list \\ \hline - \tcode{V} & \tcode{vertex_t} & & Vertex descriptor \\ - & \tcode{vertex_reference_t} & \tcode{u,v} & Vertex descriptor reference. \tcode{u} is the source (or only) vertex. \tcode{v} is the target vertex. \\ + \tcode{V} & \tcode{vertex_t} & \tcode{u,v} & Vertex descriptor. \tcode{u} is the source (or only) vertex. \tcode{v} is the target vertex. \\ \tcode{VId} & \tcode{vertex_id_t} & \tcode{uid,vid,source} & Vertex id. \tcode{uid} is the source (or only) vertex id. \tcode{vid} is the target vertex id. \\ \tcode{VV} & \tcode{vertex_value_t} & \tcode{val} & Vertex Value, value or reference. This can be either the user-defined value on a vertex, or a value returned by a function object (e.g. \tcode{VVF}) that is related to the vertex. \\ \tcode{VR} & \tcode{vertex_range_t} & \tcode{ur,vr} & Vertex Range \\ @@ -27,19 +26,18 @@ \section{Naming Conventions} & & \tcode{first,last} & \tcode{first} and \tcode{last} are the begin and end iterators of a vertex range. \\ \tcode{VVF} & & \tcode{vvf} & Vertex Value Function: \tcode{vvf(u)} $\rightarrow$ vertex value, or \tcode{vvf(uid)} $\rightarrow$ vertex value, depending on requirements of the consuming algorithm or view. \\ - \tcode{VProj} & & \tcode{vproj} & Vertex info projection function: \tcode{vproj(u)} $\rightarrow$ \tcode{vertex_info}. \\ + \tcode{VProj} & & \tcode{vproj} & Vertex data projection function: \tcode{vproj(u)} $\rightarrow$ \tcode{vertex_data}. \\ \hdashline & \tcode{partition_id_t} & \tcode{pid} & Partition id. \\ & & \tcode{P} & Number of partitions. \\ \tcode{PVR} & \tcode{partition_vertex_range_t} & \tcode{pur,pvr} & Partition vertex range. \\ \hline - \tcode{E} & \tcode{edge_t} & & Edge descriptor \\ - & \tcode{edge_reference_t} & \tcode{uv,vw} & Edge descriptor reference. \tcode{uv} is an edge from vertices \tcode{u} to \tcode{v}. \tcode{vw} is an edge from vertices \tcode{v} to \tcode{w}. \\ + \tcode{E} & \tcode{edge_t} & \tcode{uv,vw} & Edge descriptor. \tcode{uv} is an edge from vertices \tcode{u} to \tcode{v}. \tcode{vw} is an edge from vertices \tcode{v} to \tcode{w}. \\ \tcode{EV} & \tcode{edge_value_t} & \tcode{val} & Edge Value, value or reference. This can be either the user-defined value on an edge, or a value returned by a function object (e.g. \tcode{EVF}) that is related to the edge. \\ \tcode{ER} & \tcode{vertex_edge_range_t} & & Edge Range for edges of a vertex \\ \tcode{EI} & \tcode{vertex_edge_iterator_t} & \tcode{uvi,vwi} & Edge Iterator for an edge of a vertex. \tcode{uvi} is an iterator for an edge from vertices \tcode{u} to \tcode{v}. \tcode{vwi} is an iterator for an edge from vertices \tcode{v} to \tcode{w}. \\ \tcode{EVF} & & \tcode{evf} & Edge Value Function: \tcode{evf(uv)} $\rightarrow$ edge value. \\ - \tcode{EProj} & & \tcode{eproj} & Edge info projection function: \tcode{eproj(uv)} $\rightarrow$ \tcode{edge_info}. \\ + \tcode{EProj} & & \tcode{eproj} & Edge data projection function: \tcode{eproj(uv)} $\rightarrow$ \tcode{edge_data}. \\ \hline \end{tabular}} \caption{Naming Conventions for Types and Variables} diff --git a/tex/specification.tex b/tex/specification.tex index cb92bb3..6b86f32 100644 --- a/tex/specification.tex +++ b/tex/specification.tex @@ -36,7 +36,7 @@ \subsection{Functions} auto vertex_id(G&& g, vertex_iterator_t); template -auto vertex_value(G&& g, vertex_reference_t); +auto vertex_value(G&& g, vertex_t); } // namespace std::graph \end{lstlisting} @@ -55,7 +55,7 @@ \subsection{Traits} namespace std::graph { template -concept has_degree = requires(G&& g, vertex_reference_t u) { +concept has_degree = requires(G&& g, vertex_t u) { {degree(g, u)}; }; @@ -65,7 +65,7 @@ \subsection{Traits} }; template -concept has_find_vertex_edge = requires(G&& g, vertex_id_t uid, vertex_id_t vid, vertex_reference_t u) { +concept has_find_vertex_edge = requires(G&& g, vertex_id_t uid, vertex_id_t vid, vertex_t u) { { find_vertex_edge(g, u, vid) } -> forward_iterator; { find_vertex_edge(g, uid, vid) } -> forward_iterator; }; @@ -104,7 +104,7 @@ \subsection{Concepts} template concept adjacency_list = vertex_range && targeted_edge> && - requires(G&& g, vertex_reference_t u, vertex_id_t uid) { + requires(G&& g, vertex_t u, vertex_id_t uid) { { edges(g, u) } -> forward_range; { edges(g, uid) } -> forward_range; }; @@ -131,10 +131,10 @@ \subsection{Header \tcode{} synopsis [graph.syn]} auto vertexlist(G&& g); template -auto incidence(G&& g, vertex_reference_t); +auto incidence(G&& g, vertex_t); template -auto neighbors(G&& g, vertex_reference_t); +auto neighbors(G&& g, vertex_t); template auto edgelist(G&& g);