Metall v0.30
A persistent memory allocator for data-centric analytics
 
Loading...
Searching...
No Matches
jgraph.hpp
Go to the documentation of this file.
1// Copyright 2021 Lawrence Livermore National Security, LLC and other Metall
2// Project Developers. See the top-level COPYRIGHT file for details.
3//
4// SPDX-License-Identifier: (Apache-2.0 OR MIT)
5
6#ifndef METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
7#define METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
8
9#include <iostream>
10#include <string_view>
11#include <functional>
12#include <utility>
13#include <optional>
14
19#include <metall/json/json.hpp>
20
24
29
30namespace {
31namespace mc = metall::container;
32namespace mj = metall::json;
33} // namespace
34
35// --- Forward declarations --- //
36template <typename allocator_type>
37class jgraph;
38
39namespace jgdtl {
40template <typename storage_iterator_type>
42
43template <typename adj_list_edge_list_iterator_type,
44 typename storage_pointer_type>
46} // namespace jgdtl
47
48template <typename _allocator_type = std::allocator<std::byte>>
49class jgraph {
50 public:
52
54 using id_type = std::string_view;
55
58
59 private:
60 template <typename T>
61 using other_allocator =
62 typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
63
64 template <typename T>
65 using other_scoped_allocator =
67
68 using string_type =
70 typename std::allocator_traits<
72
73 using internal_id_type = uint64_t;
74 static constexpr internal_id_type k_max_internal_id =
75 std::numeric_limits<internal_id_type>::max();
76
77 class vertex_data_type {
78 public:
80
81 explicit vertex_data_type(
82 const id_type &id, const allocator_type &allocator = allocator_type())
83 : m_id(id, allocator), m_value(allocator) {}
84
86 vertex_data_type(const vertex_data_type &) = default;
87
89 vertex_data_type(const vertex_data_type &other, const allocator_type &alloc)
90 : m_id(other.m_id, alloc), m_value(other.m_value, alloc) {}
91
93 vertex_data_type(vertex_data_type &&) noexcept = default;
94
96
97 vertex_data_type(vertex_data_type &&other,
98 const allocator_type &alloc) noexcept
99 : m_id(std::move(other.m_id), alloc),
100 m_value(std::move(other.m_value), alloc) {}
101
103 vertex_data_type &operator=(const vertex_data_type &) = default;
104
106 vertex_data_type &operator=(vertex_data_type &&) noexcept = default;
107
108 const id_type id() const { return id_type(m_id); }
109
110 value_type &value() { return m_value; }
111
112 const value_type &value() const { return m_value; }
113
114 private:
115 string_type m_id;
116 value_type m_value;
117 };
118
119 using vertex_storage_type =
120 mc::unordered_map<internal_id_type, vertex_data_type,
121 metall::utility::hash<>, std::equal_to<>,
122 other_scoped_allocator<std::pair<const internal_id_type,
123 vertex_data_type>>>;
124
125 class edge_data_type {
126 public:
127 using allocator_type = _allocator_type;
128
129 explicit edge_data_type(const id_type &source_id,
130 const id_type &destination_id,
131 const internal_id_type &edge_id,
132 const allocator_type &allocator = allocator_type())
133 : m_source_id(source_id, allocator),
134 m_destination_id(destination_id, allocator),
135 m_edge_id(edge_id),
136 m_value(allocator) {}
137
139 edge_data_type(const edge_data_type &) = default;
140
142
143 edge_data_type(const edge_data_type &other, const allocator_type &alloc)
144 : m_source_id(other.m_source_id, alloc),
145 m_destination_id(other.m_destination_id, alloc),
146 m_edge_id(other.m_edge_id),
147 m_value(other.m_value, alloc) {}
148
150 edge_data_type(edge_data_type &&) noexcept = default;
151
153
154 edge_data_type(edge_data_type &&other, const allocator_type &alloc) noexcept
155 : m_source_id(other.m_source_id, alloc),
156 m_destination_id(other.m_destination_id, alloc),
157 m_edge_id(other.m_edge_id),
158 m_value(other.m_value, alloc) {}
159
161 edge_data_type &operator=(const edge_data_type &) = default;
162
164 edge_data_type &operator=(edge_data_type &&) noexcept = default;
165
166 const id_type source_id() const { return id_type(m_source_id); }
167
168 const id_type destination_id() const { return id_type(m_destination_id); }
169
170 // const id_type edge_id() const {
171 // return id_type(m_edge_id);
172 // }
173
174 value_type &value() { return m_value; }
175
176 const value_type &value() const { return m_value; }
177
178 private:
179 string_type m_source_id;
180 string_type m_destination_id;
181 internal_id_type m_edge_id;
182 value_type m_value;
183 };
184
185 using edge_storage_type =
186 mc::unordered_map<internal_id_type, edge_data_type,
187 metall::utility::hash<>, std::equal_to<>,
188 other_scoped_allocator<
189 std::pair<const internal_id_type, edge_data_type>>>;
190
191 // Adj-list
192 using adj_list_edge_list_type =
193 mc::unordered_multimap<internal_id_type, // hashed destination vid
194 internal_id_type, // edge id
195 std::hash<internal_id_type>, std::equal_to<>,
196 other_scoped_allocator<std::pair<
197 const internal_id_type, internal_id_type>>>;
198
199 using adj_list_type = mc::unordered_map<
200 internal_id_type, // hashed source vid
201 adj_list_edge_list_type, std::hash<internal_id_type>, std::equal_to<>,
202 other_scoped_allocator<
203 std::pair<const internal_id_type, adj_list_edge_list_type>>>;
204
205 // ID tables
206 using id_table_type = mc::unordered_map<
207 internal_id_type, string_type, std::hash<internal_id_type>,
208 std::equal_to<>,
210
211 public:
216
220
224 typename adj_list_edge_list_type::iterator,
225 typename std::pointer_traits<typename std::allocator_traits<
229 typename adj_list_edge_list_type::const_iterator,
230 typename std::pointer_traits<typename std::allocator_traits<
232
236 : m_vertex_storage(alloc),
237 m_edge_storage(alloc),
238 m_adj_list(alloc),
239 m_vertex_id_table(alloc) {}
240
244 bool has_vertex(const id_type &vertex_id) const {
245 return priv_get_vertex_internal_id(vertex_id) != k_max_internal_id;
246 }
247
249 const id_type &destination_vertex_id) const {
250 const auto src = priv_get_vertex_internal_id(source_vertex_id);
251 if (src == k_max_internal_id) return 0;
252
253 const auto dst = priv_get_vertex_internal_id(destination_vertex_id);
254 if (dst == k_max_internal_id) return 0;
255
256 const auto &edge_list = m_adj_list.at(src);
257 return edge_list.count(dst);
258 }
259
261 auto internal_id = priv_get_vertex_internal_id(vertex_id);
262 if (internal_id != k_max_internal_id) {
263 return vertex_iterator(m_vertex_storage.find(internal_id));
264 }
265
266 internal_id = priv_generate_vertex_internal_id(vertex_id);
267
268 m_adj_list.emplace(internal_id,
269 adj_list_edge_list_type{m_adj_list.get_allocator()});
270
271 auto ret = m_vertex_storage.emplace(
273 vertex_data_type{vertex_id, m_vertex_storage.get_allocator()});
274 return vertex_iterator(ret.first);
275 }
276
279 const bool undirected = false) {
282
283 const auto src_internal_id = priv_get_vertex_internal_id(source_vertex_id);
284 assert(src_internal_id != k_max_internal_id);
285 const auto dst_internal_id =
286 priv_get_vertex_internal_id(destination_vertex_id);
287 assert(dst_internal_id != k_max_internal_id);
288 const auto edge_id = priv_generate_edge_id();
289
290 auto itr = m_adj_list.at(src_internal_id).emplace(dst_internal_id, edge_id);
291 if (undirected) {
292 m_adj_list.at(dst_internal_id).emplace(src_internal_id, edge_id);
293 }
294
295 m_edge_storage.emplace(
297 edge_id, m_edge_storage.get_allocator()});
298 return edge_iterator(itr, &m_edge_storage);
299 }
300
302 return vertex_iterator(
303 m_vertex_storage.find(priv_get_vertex_internal_id(vertex_id)));
304 }
305
308 m_vertex_storage.find(priv_get_vertex_internal_id(vertex_id)));
309 }
310
311 std::pair<edge_iterator, edge_iterator> find_edges(
313 const auto src_internal_id = priv_get_vertex_internal_id(source_vertex_id);
314 const auto dst_internal_id =
315 priv_get_vertex_internal_id(destination_vertex_id);
316
317 if (src_internal_id == k_max_internal_id ||
318 dst_internal_id == k_max_internal_id) {
319 return std::make_pair(edge_iterator{}, edge_iterator{});
320 }
321
322 auto &edge_list = m_adj_list.at(src_internal_id);
323 auto range = edge_list.equal_range(dst_internal_id);
324 return std::make_pair(edge_iterator{range.first, &m_edge_storage},
325 edge_iterator{range.second, &m_edge_storage});
326 }
327
330 std::size_t num_vertices() const { return m_vertex_storage.size(); }
331
334 std::size_t num_edges() const { return m_edge_storage.size(); }
335
340 std::size_t degree(const id_type &vertex_id) const {
341 const auto internal_id = priv_get_vertex_internal_id(vertex_id);
342 if (internal_id == k_max_internal_id) {
343 return 0;
344 }
345
346 return m_adj_list.at(internal_id).size();
347 }
348
350 return vertex_iterator(m_vertex_storage.begin());
351 }
352
354 return const_vertex_iterator(m_vertex_storage.begin());
355 }
356
358 return vertex_iterator(m_vertex_storage.end());
359 }
360
362 return const_vertex_iterator(m_vertex_storage.end());
363 }
364
366 const auto internal_id = priv_get_vertex_internal_id(vid);
367 if (internal_id == k_max_internal_id) {
368 return edge_iterator();
369 }
370 return edge_iterator{m_adj_list.at(internal_id).begin(), &m_edge_storage};
371 }
372
374 const auto internal_id = priv_get_vertex_internal_id(vid);
375 if (internal_id == k_max_internal_id) {
376 return const_edge_iterator();
377 }
378 return const_edge_iterator{m_adj_list.at(internal_id).begin(),
379 &m_edge_storage};
380 }
381
383 const auto internal_id = priv_get_vertex_internal_id(vid);
384 if (internal_id == k_max_internal_id) {
385 return edge_iterator();
386 }
387 return edge_iterator{m_adj_list.at(internal_id).end(), &m_edge_storage};
388 }
389
391 const auto internal_id = priv_get_vertex_internal_id(vid);
392 if (internal_id == k_max_internal_id) {
393 return const_edge_iterator();
394 }
395 return const_edge_iterator{m_adj_list.at(internal_id).end(),
396 &m_edge_storage};
397 }
398
400 return m_vertex_storage.get_allocator();
401 }
402
403 private:
404 internal_id_type priv_get_vertex_internal_id(
405 const std::string_view &vid) const {
406 auto hash = priv_hash_id(vid.data());
407
408 for (std::size_t d = 0; d <= m_max_vid_distance; ++d) {
409 const auto vitr = m_vertex_id_table.find(hash);
410 if (vitr == m_vertex_id_table.end()) {
411 break;
412 }
413
414 if (vitr->second == vid) {
415 return hash;
416 }
417 hash = (hash + 1) % k_max_internal_id;
418 }
419
420 return k_max_internal_id; // Couldn't find
421 }
422
423 internal_id_type priv_generate_vertex_internal_id(
424 const std::string_view &vid) {
425 auto hash = priv_hash_id(vid.data());
426
427 std::size_t distance = 0;
428 while (m_vertex_id_table.count(hash) > 0) {
429 hash = (hash + 1) % k_max_internal_id;
430 ++distance;
431 }
432 m_max_vid_distance = std::max(distance, m_max_vid_distance);
433
434 m_vertex_id_table[hash] = vid.data();
435
436 return hash;
437 }
438
439 internal_id_type priv_generate_edge_id() { return ++m_max_edge_id; }
440
441 static internal_id_type priv_hash_id(const std::string_view &id) {
442 return metall::mtlldetail::murmur_hash_64a(id.data(), id.length(), 1234);
443 }
444
445 vertex_storage_type m_vertex_storage;
446 edge_storage_type m_edge_storage;
447 adj_list_type m_adj_list;
448 id_table_type m_vertex_id_table;
449 internal_id_type m_max_edge_id{0};
450 std::size_t m_max_vid_distance{0};
451};
452
453namespace jgdtl {
454
455template <typename storage_iterator_type>
457 private:
458 static constexpr bool is_const_value = std::is_const_v<
459 typename std::iterator_traits<storage_iterator_type>::value_type>;
460
461 // Memo: this type will be always non-const even element_type is const
462 using raw_value_type = typename std::tuple_element<
463 1,
464 typename std::iterator_traits<storage_iterator_type>::value_type>::type;
465
466 public:
468 std::conditional_t<is_const_value, const raw_value_type, raw_value_type>;
469 using pointer = typename std::pointer_traits<typename std::iterator_traits<
473 typename std::iterator_traits<storage_iterator_type>::difference_type;
474
477
480
483
485 ++m_current_pos;
486 return *this;
487 }
488
491 operator++();
492 return tmp;
493 }
494
495 bool equal(const vertex_iterator_impl &other) const {
496 return m_current_pos == other.m_current_pos;
497 }
498
499 pointer operator->() { return const_cast<pointer>(&(m_current_pos->second)); }
500
501 const pointer operator->() const { return &(m_current_pos->second); }
502
503 reference operator*() { return m_current_pos->second; }
504
505 const reference operator*() const { return m_current_pos->second; }
506
507 private:
508 storage_iterator_type m_current_pos;
509};
510
511template <typename storage_iterator_type>
514 return lhs.equal(rhs);
515}
516
517template <typename storage_iterator_type>
520 return !(lhs == rhs);
521}
522
523template <typename adj_list_edge_list_iterator_type,
524 typename storage_pointer_type>
526 private:
527 static constexpr bool is_const_value = std::is_const_v<
528 typename std::pointer_traits<storage_pointer_type>::element_type>;
529
530 // Memo: this type will be always non-const even element_type is const
531 using raw_value_type = typename std::pointer_traits<
532 storage_pointer_type>::element_type::mapped_type;
533
534 public:
536 std::conditional_t<is_const_value, const raw_value_type, raw_value_type>;
537 using pointer = typename std::pointer_traits<
538 typename std::pointer_traits<storage_pointer_type>::element_type::
539 iterator::pointer>::template rebind<value_type>;
541 using difference_type = typename std::iterator_traits<
543
544 edge_iterator_impl() : m_current_pos(), m_storage_pointer(nullptr) {}
545
549
552
555
557 ++m_current_pos;
558 return *this;
559 }
560
562 edge_iterator_impl tmp(*this);
563 operator++();
564 return tmp;
565 }
566
567 bool equal(const edge_iterator_impl &other) const {
568 return m_current_pos == other.m_current_pos;
569 }
570
572 const auto &edge_id = m_current_pos->second;
573 return &(m_storage_pointer->at(edge_id));
574 }
575
576 const pointer operator->() const {
577 const auto &edge_id = m_current_pos->second;
578 return &(m_storage_pointer->at(edge_id));
579 }
580
582 const auto &edge_id = m_current_pos->second;
583 return (m_storage_pointer->at(edge_id));
584 }
585
586 const reference operator*() const {
587 const auto &edge_id = m_current_pos->second;
588 return (m_storage_pointer->at(edge_id));
589 }
590
591 private:
593 storage_pointer_type m_storage_pointer;
594};
595
596template <typename adj_list_edge_list_iterator_type,
597 typename storage_pointer_type>
598inline bool operator==(
599 const edge_iterator_impl<adj_list_edge_list_iterator_type,
601 const edge_iterator_impl<adj_list_edge_list_iterator_type,
603 return lhs.equal(rhs);
604}
605
606template <typename adj_list_edge_list_iterator_type,
607 typename storage_pointer_type>
608inline bool operator!=(
609 const edge_iterator_impl<adj_list_edge_list_iterator_type,
611 const edge_iterator_impl<adj_list_edge_list_iterator_type,
613 return !(lhs == rhs);
614}
615
616} // namespace jgdtl
617
618} // namespace metall::container::experimental::jgraph
619
622
623#endif // METALL_CONTAINER_EXPERIMENT_JGRAPH_JGRAPH_HPP
edge_iterator_impl(adj_list_edge_list_iterator_type begin_pos, storage_pointer_type storage)
Definition jgraph.hpp:546
edge_iterator_impl & operator++()
Definition jgraph.hpp:556
const pointer operator->() const
Definition jgraph.hpp:576
bool equal(const edge_iterator_impl &other) const
Definition jgraph.hpp:567
edge_iterator_impl operator++(int)
Definition jgraph.hpp:561
typename std::pointer_traits< typename std::pointer_traits< storage_pointer_type >::element_type::iterator::pointer >::template rebind< value_type > pointer
Definition jgraph.hpp:539
edge_iterator_impl(edge_iterator_impl &&) noexcept=default
typename std::iterator_traits< adj_list_edge_list_iterator_type >::difference_type difference_type
Definition jgraph.hpp:542
std::conditional_t< is_const_value, const raw_value_type, raw_value_type > value_type
Definition jgraph.hpp:536
const reference operator*() const
Definition jgraph.hpp:586
vertex_iterator_impl(vertex_iterator_impl &&) noexcept=default
typename std::iterator_traits< storage_iterator_type >::difference_type difference_type
Definition jgraph.hpp:473
bool equal(const vertex_iterator_impl &other) const
Definition jgraph.hpp:495
vertex_iterator_impl operator++(int)
Definition jgraph.hpp:489
const reference operator*() const
Definition jgraph.hpp:505
std::conditional_t< is_const_value, const raw_value_type, raw_value_type > value_type
Definition jgraph.hpp:468
vertex_iterator_impl(storage_iterator_type begin_pos)
Definition jgraph.hpp:475
typename std::pointer_traits< typename std::iterator_traits< storage_iterator_type >::pointer >::template rebind< value_type > pointer
Definition jgraph.hpp:470
vertex_iterator_impl & operator++()
Definition jgraph.hpp:484
const pointer operator->() const
Definition jgraph.hpp:501
const_edge_iterator edges_begin(const id_type &vid) const
Definition jgraph.hpp:373
const_vertex_iterator vertices_begin() const
Definition jgraph.hpp:353
jgdtl::vertex_iterator_impl< typename vertex_storage_type::const_iterator > const_vertex_iterator
Const vertex iterator.
Definition jgraph.hpp:219
std::string_view id_type
The type of vertex ID and edge ID.
Definition jgraph.hpp:54
std::size_t num_vertices() const
Returns the number of vertices.
Definition jgraph.hpp:330
jgdtl::edge_iterator_impl< typename adj_list_edge_list_type::iterator, typename std::pointer_traits< typename std::allocator_traits< allocator_type >::pointer >::template rebind< edge_storage_type > > edge_iterator
Edge iterator over a container of edge data, which is metall::container::experimental::json::key_valu...
Definition jgraph.hpp:226
jgdtl::vertex_iterator_impl< typename vertex_storage_type::iterator > vertex_iterator
Vertex iterator over a container of vertex data, which is metall::container::experimental::json::key_...
Definition jgraph.hpp:215
_allocator_type allocator_type
Definition jgraph.hpp:51
const_vertex_iterator find_vertex(const id_type &vertex_id) const
Definition jgraph.hpp:306
edge_iterator edges_begin(const id_type &vid)
Definition jgraph.hpp:365
vertex_iterator register_vertex(const id_type &vertex_id)
Definition jgraph.hpp:260
allocator_type get_allocator() const
Definition jgraph.hpp:399
edge_iterator register_edge(const id_type &source_vertex_id, const id_type &destination_vertex_id, const bool undirected=false)
Definition jgraph.hpp:277
jgraph(const allocator_type &alloc=allocator_type())
Constructor.
Definition jgraph.hpp:235
jgdtl::edge_iterator_impl< typename adj_list_edge_list_type::const_iterator, typename std::pointer_traits< typename std::allocator_traits< allocator_type >::pointer >::template rebind< const edge_storage_type > > const_edge_iterator
Const edge iterator.
Definition jgraph.hpp:231
bool has_vertex(const id_type &vertex_id) const
Checks if a vertex exists.
Definition jgraph.hpp:244
const_vertex_iterator vertices_end() const
Definition jgraph.hpp:361
vertex_iterator find_vertex(const id_type &vertex_id)
Definition jgraph.hpp:301
const_edge_iterator edges_end(const id_type &vid) const
Definition jgraph.hpp:390
vertex_iterator vertices_end()
Definition jgraph.hpp:357
mj::value< allocator_type > value_type
JSON value type every vertex and edge has,.
Definition jgraph.hpp:57
std::size_t num_edges() const
Returns the number of edges.
Definition jgraph.hpp:334
std::pair< edge_iterator, edge_iterator > find_edges(const id_type &source_vertex_id, const id_type &destination_vertex_id)
Definition jgraph.hpp:311
edge_iterator edges_end(const id_type &vid)
Definition jgraph.hpp:382
vertex_iterator vertices_begin()
Definition jgraph.hpp:349
std::size_t has_edges(const id_type &source_vertex_id, const id_type &destination_vertex_id) const
Definition jgraph.hpp:248
std::size_t degree(const id_type &vertex_id) const
Returns the degree of the vertex corresponds to 'vid'.
Definition jgraph.hpp:340
Namespace for Metall JSON graph container, which is in an experimental phase.
Namespace for Metall containers in an experimental phase.
Namespace for Metall container.
boost::container::vector< T, Allocator > vector
A vector container that uses Metall as its default allocator.
Definition vector.hpp:17
boost::unordered_map< Key, T, Hash, KeyEqual, Allocator > unordered_map
An unordered_map container that uses Metall as its default allocator.
Definition unordered_map.hpp:21
boost::unordered_multimap< Key, T, Hash, KeyEqual, Allocator > unordered_multimap
An unordered_multimap container that uses Metall as its default allocator.
Definition unordered_map.hpp:29
Namespace for Metall JSON container, which is in an experimental phase.
Definition array.hpp:17
metall::mtlldetail::hash< seed > hash
Hash a value of type T.
Definition hash.hpp:18
bool operator!=(const stl_allocator< T, kernel > &rhd, const stl_allocator< T, kernel > &lhd)
Definition stl_allocator.hpp:238
bool operator==(const stl_allocator< T, kernel > &rhd, const stl_allocator< T, kernel > &lhd)
Definition stl_allocator.hpp:230