aGrUM 3.1.1
a C++ library for (probabilistic) graphical models
fastGraph_tpl.h
Go to the documentation of this file.
1/****************************************************************************
2 * This file is part of the aGrUM/pyAgrum library. *
3 * *
4 * Copyright (c) 2005-2026 by *
5 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
6 * - Christophe GONZALES(_at_AMU) *
7 * *
8 * The aGrUM/pyAgrum library is free software; you can redistribute it *
9 * and/or modify it under the terms of either : *
10 * *
11 * - the GNU Lesser General Public License as published by *
12 * the Free Software Foundation, either version 3 of the License, *
13 * or (at your option) any later version, *
14 * - the MIT license (MIT), *
15 * - or both in dual license, as here. *
16 * *
17 * (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) *
18 * *
19 * This aGrUM/pyAgrum library is distributed in the hope that it will be *
20 * useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
21 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26 * OTHER DEALINGS IN THE SOFTWARE. *
27 * *
28 * See LICENCES for more details. *
29 * *
30 * SPDX-FileCopyrightText: Copyright 2005-2026 *
31 * - Pierre-Henri WUILLEMIN(_at_LIP6) *
32 * - Christophe GONZALES(_at_AMU) *
33 * SPDX-License-Identifier: LGPL-3.0-or-later OR MIT *
34 * *
35 * Contact : info_at_agrum_dot_org *
36 * homepage : http://agrum.gitlab.io *
37 * gitlab : https://gitlab.com/agrumery/agrum *
38 * *
39 ****************************************************************************/
40
41#pragma once
42
43#include <algorithm>
44#include <cstdint>
45#include <string>
46#include <utility>
47#include <vector>
48
50
52
53namespace gum {
54
55 namespace detail {
56
58 enum class FastGraphOp : std::uint8_t { None, Arc, RevArc, Edge };
59
69 inline std::size_t fastGraphFindDash(std::string_view chain, std::size_t from) {
70 int depth = 0;
71 for (std::size_t i = from; i < chain.size(); ++i) {
72 const char c = chain[i];
73 if (c == '[' || c == '{') {
74 ++depth;
75 } else if (c == ']' || c == '}') {
76 if (depth > 0) --depth;
77 } else if ((c == '-') && (depth == 0)) {
78 return i;
79 }
80 }
81 return std::string_view::npos;
82 }
83
95 inline std::vector< std::pair< std::string, FastGraphOp > >
96 fastGraphTokenize(std::string_view chain) {
97 std::vector< std::pair< std::string, FastGraphOp > > tokens;
98 std::size_t pos = 0;
100
101 while (pos <= chain.size()) {
102 const auto dashPos = fastGraphFindDash(chain, pos);
103
104 std::size_t opStart = dashPos;
105 std::size_t opLen = 1;
107
108 if (dashPos != std::string_view::npos) {
109 if ((dashPos + 1 < chain.size()) && (chain[dashPos + 1] == '>')) {
110 opLen = 2;
111 op = FastGraphOp::Arc;
112 } else if ((dashPos > pos) && (chain[dashPos - 1] == '<')) {
113 opStart = dashPos - 1;
114 opLen = 2;
116 }
117 }
118
119 const auto tokEnd = (dashPos == std::string_view::npos) ? chain.size() : opStart;
120
121 tokens.emplace_back(trim_copy(chain.substr(pos, tokEnd - pos)), pendingOp);
122
123 if (dashPos == std::string_view::npos) { break; }
124 pendingOp = op;
125 pos = opStart + opLen;
126 }
127 return tokens;
128 }
129
131 inline bool fastGraphIsNodeIdToken(const std::string& token) {
132 int value = 0;
133 return isIntegerWithResult(token, &value) && (value >= 0);
134 }
135
137 template < typename GRAPH_TYPE >
138 NodeId fastGraphBuildNode(GRAPH_TYPE& g, const std::string& token, bool useIds) {
139 if (useIds) {
140 int value = 0;
141 isIntegerWithResult(token, &value);
142 const auto id = static_cast< NodeId >(value);
143 if (!g.existsNode(id)) { g.addNodeWithId(id); }
144 return id;
145 }
146
147 if (const auto id = g.idFromName(token)) { return *id; }
148 const NodeId id = g.addNode();
149 g.setName(id, token);
150 return id;
151 }
152
175 template < typename Resolve, typename AddArc, typename AddEdge >
176 void fastGraphWalkTokens(const std::vector< std::pair< std::string, FastGraphOp > >& tokens,
177 std::string_view desc,
178 Resolve resolve,
179 AddArc addArc,
180 AddEdge addEdge) {
181 NodeId lastId = 0;
182 bool first = true;
183
184 for (const auto& [token, op]: tokens) {
185 if (token.empty()) {
187 "fastGraph: malformed description '" << desc
188 << "' (operator without a node token)")
189 }
190 const NodeId id = resolve(token);
191
192 if (!first) {
193 if (op == FastGraphOp::Arc) {
194 addArc(lastId, id, token);
195 } else if (op == FastGraphOp::RevArc) {
196 addArc(id, lastId, token);
197 } else {
198 addEdge(lastId, id, token);
199 }
200 }
201
202 lastId = id;
203 first = false;
204 }
205 }
206
207 } // namespace detail
208
209 template < GUM_NodeGraphable GRAPH_TYPE >
210 GRAPH_TYPE fastGraph(std::string_view desc) {
211 using namespace detail;
212
213 std::vector< std::vector< std::pair< std::string, FastGraphOp > > > chains;
214 bool useIds = true;
215
216 for (const auto& chainStr: split(remove_newline(desc), ";")) {
217 if (trim_copy(chainStr).empty()) { continue; }
218
219 auto tokens = fastGraphTokenize(chainStr);
220 for (const auto& [token, op]: tokens) {
221 if (token.empty()) {
223 "fastGraph: malformed description '" << desc
224 << "' (operator without a node token)")
225 }
226 if (!fastGraphIsNodeIdToken(token)) { useIds = false; }
227 }
228 chains.push_back(std::move(tokens));
229 }
230
231 GRAPH_TYPE g;
232
233 auto resolve = [&](const std::string& token) { return fastGraphBuildNode(g, token, useIds); };
234 auto addArc = [&](NodeId tail, NodeId head, const std::string& token) {
235 if constexpr (GUM_DiGraphable< GRAPH_TYPE >) {
236 g.addArc(tail, head);
237 } else {
239 "fastGraph: '" << token << "' is preceded by an arc operator but the "
240 << "requested graph type does not support arcs")
241 }
242 };
243 auto addEdge = [&](NodeId a, NodeId b, const std::string& token) {
244 if constexpr (GUM_UndiGraphable< GRAPH_TYPE >) {
245 g.addEdge(a, b);
246 } else {
248 "fastGraph: '" << token << "' is preceded by '-' but the "
249 << "requested graph type does not support edges")
250 }
251 };
252
253 for (const auto& tokens: chains) {
254 fastGraphWalkTokens(tokens, desc, resolve, addArc, addEdge);
255 }
256
257 return g;
258 }
259
260} // namespace gum
The base class for all directed edges.
The base class for all undirected edges.
Exception : there is something wrong with an arc.
Exception : there is something wrong with an edge.
Concept for directed graphs (arcs with parents/children).
Concept for undirected graphs (edges with neighbours).
#define GUM_ERROR(type, msg)
Definition exceptions.h:76
Builds a graph from a "fast" DOT-like textual description.
Size NodeId
Type for node ids.
std::string remove_newline(std::string_view s)
remove all newlines in a string
bool isIntegerWithResult(std::string_view val, int *res)
return true is a string contains an integer value
std::vector< std::string > split(std::string_view str, std::string_view delim)
Split str using the delimiter.
std::string trim_copy(std::string_view s)
trim from both ends (copying)
FastGraphOp
which operator (if any) precedes a node token in a fastGraph chain
std::size_t fastGraphFindDash(std::string_view chain, std::size_t from)
Finds the first '-' at bracket-depth 0, starting at from, skipping any '['...']' or '{'....
void fastGraphWalkTokens(const std::vector< std::pair< std::string, FastGraphOp > > &tokens, std::string_view desc, Resolve resolve, AddArc addArc, AddEdge addEdge)
Walks one already-tokenized chain (see fastGraphTokenize()), resolving each token to a NodeId via res...
NodeId fastGraphBuildNode(GRAPH_TYPE &g, const std::string &token, bool useIds)
gets or creates the node denoted by token, in NodeId or name mode
std::vector< std::pair< std::string, FastGraphOp > > fastGraphTokenize(std::string_view chain)
Scans one ";"-delimited chain, splitting it into node tokens while remembering, for each token,...
bool fastGraphIsNodeIdToken(const std::string &token)
true if token must be read as a NodeId (non-negative integer)
gum is the global namespace for all aGrUM entities
Definition agrum.h:46
GRAPH_TYPE fastGraph(std::string_view desc)
Builds a GRAPH_TYPE from a DOT-like textual description.
Utilities for manipulating strings.