Abstract Graphs

Like abstract datasets, the graph concepts svs::graphs::ImmutableMemoryGraph and svs::graphs::MemoryGraph are used to model the expected behavior of graphs. Concrete implementations can be found here.

Main Concepts

template<typename T>
concept ImmutableMemoryGraph
#include <svs/concepts/graph.h>

Main concept modeling immutable in-memory graphs.

template <typename T>
concept ImmutableMemoryGraph = requires(const T& const_g) {
    // The encoding of vertices in the graph.
    // At the time of writing, this is expected to be an integer.
    // This may be relaxed in the future.
    typename T::index_type;
    std::integral<typename T::index_type>;

    // Has `reference` and `const_reference` type aliases.
    // These types should be at least forward ranges, but preferably random access
    // ranges.
    //
    // Items yieled by the iterators for these ranges should ``index_type``.
    typename T::reference;
    typename T::const_reference;

    // Return the maximum degree that this particular implementation of the graph is
    // capable of supporting.
    //
    // If the graph supports unbounded adjacency lists, may return `Dynamic`.
    { const_g.max_degree() } -> std::convertible_to<size_t>;

    // Return the number of vertices contained in traph.
    { const_g.n_nodes() } -> std::convertible_to<size_t>;

    // Adjacency list operations.
    requires requires(typename T::index_type i) {
        // Return a range over the adjacency list for node ``i``.
        { const_g.get_node(i) } -> std::same_as<typename T::const_reference>;

        // Return the number of out neighbors for node ``i``.
        { const_g.get_node_degree(i) } -> std::convertible_to<size_t>;

        // Prefetch the adjacency list for node ``i``.
        // This is a performance optimization only and may be implemented as a no-op
        // without affecting correctness.
        const_g.prefetch_node(i);
    };
};
template<typename T>
concept MemoryGraph
#include <svs/concepts/graph.h>

Concept modeling mutable in-memory graphs.

template <typename T>
concept MemoryGraph = requires(T& g, const T& const_g) {
    // Add an edge to the graph.
    // Must return the out degree of `src` after adding the edge `src -> dst`.
    // If adding the edge would result in the graph exceeding its maximum degree,
    // implementations are free to not add this edge.
    requires requires(index_type_t<T> src, index_type_t<T> dst) {
        { g.add_edge(src, dst) } -> std::convertible_to<size_t>;
    };

    // Completely clear the adjacency list for vertex ``i``.
    requires requires(index_type_t<T> i) {
        g.clear_node(i);
    };

    // Overwrite the adjacency list for `src`.
    requires requires(
        index_type_t<T> src,
        const std::vector<index_type_t<T>>& neighbors_vector,
        std::span<const index_type_t<T>> neighbors_span
    ) {
        g.replace_node(src, neighbors_vector);
        g.replace_node(src, neighbors_span);
    };
};

Public API

template<ImmutableMemoryGraph G>
using index_type_t = typename G::index_type

Obtain the index type used to encode neighbors in the graph type G.

template<ImmutableMemoryGraph Graph1, ImmutableMemoryGraph Graph2>
bool graphs_equal(const Graph1 &x, const Graph2 &y)

Compare the equality of two graphs.

Two graphs are considered equal if:

  • The contain the same number of vertices.

  • The adjacency lists for each vertex compare equal.