2 #include <ossia/dataflow/bench_map.hpp>
3 #include <ossia/dataflow/data_copy.hpp>
4 #include <ossia/dataflow/dataflow.hpp>
5 #include <ossia/dataflow/execution_state.hpp>
6 #include <ossia/dataflow/for_each_port.hpp>
7 #include <ossia/dataflow/graph/breadth_first_search.hpp>
8 #include <ossia/dataflow/graph/graph_interface.hpp>
9 #include <ossia/dataflow/graph/graph_ordering.hpp>
10 #include <ossia/dataflow/graph/small_graph.hpp>
11 #include <ossia/dataflow/graph_edge.hpp>
12 #include <ossia/dataflow/graph_node.hpp>
13 #include <ossia/dataflow/port.hpp>
15 #include <ossia/detail/flat_set.hpp>
16 #include <ossia/detail/ptr_set.hpp>
19 #include <boost/graph/adjacency_list.hpp>
20 #include <boost/predef.h>
23 #include <boost/graph/topological_sort.hpp>
25 #include <ankerl/unordered_dense.h>
27 #if BOOST_LIB_STD_GNU >= BOOST_VERSION_NUMBER(13, 0, 0) \
28 && BOOST_VERSION_NUMBER <= BOOST_VERSION_NUMBER(1, 83, 0)
29 #define OSSIA_SMALL_VECTOR_ALLOCATOR_REBIND_FAILS 1
35 using graph_t = boost::adjacency_list<
36 boost::smallvecS, boost::smallvecS, boost::directedS, node_ptr,
37 std::shared_ptr<graph_edge>>;
39 namespace boost::detail
43 class stored_edge_property<unsigned long, std::shared_ptr<ossia::graph_edge>>
44 :
public stored_edge<unsigned long>
46 using Vertex =
unsigned long;
47 using Property = std::shared_ptr<ossia::graph_edge>;
48 typedef stored_edge_property
self;
49 typedef stored_edge<Vertex> Base;
52 typedef Property property_type;
53 inline stored_edge_property() { }
54 inline stored_edge_property(Vertex target, Property p = {})
55 : stored_edge<Vertex>(target)
56 , m_property(std::move(p))
59 stored_edge_property(
self&& x) noexcept
60 : Base(
static_cast<Base&&
>(x))
61 , m_property(std::move(x.m_property))
64 stored_edge_property(
self const& x)
65 : Base(static_cast<Base const&>(x))
66 , m_property(std::move(const_cast<self&>(x).m_property))
69 self& operator=(
self&& x)
73 static_cast<Base&
>(*this) =
static_cast<Base&&
>(x);
74 m_property = std::move(x.m_property);
77 self& operator=(
self const& x)
81 static_cast<Base&
>(*this) =
static_cast<Base const&
>(x);
82 m_property = std::move(
const_cast<self&
>(x).m_property);
85 inline Property& get_property() noexcept {
return m_property; }
86 inline const Property& get_property() const noexcept {
return m_property; }
99 using DFSVertexInfo = std::pair<
100 typename graph_traits<G>::vertex_descriptor,
102 boost::optional<typename graph_traits<G>::edge_descriptor>,
104 typename graph_traits<G>::out_edge_iterator,
105 typename graph_traits<G>::out_edge_iterator>>>;
107 template <
class Inc
idenceGraph,
class DFSVisitor,
class ColorMap>
108 void custom_depth_first_visit_impl(
109 const IncidenceGraph& g,
typename graph_traits<IncidenceGraph>::vertex_descriptor u,
110 DFSVisitor& vis, ColorMap color, std::vector<DFSVertexInfo<IncidenceGraph>>& stack)
112 constexpr detail::nontruth2 func;
113 BOOST_CONCEPT_ASSERT((IncidenceGraphConcept<IncidenceGraph>));
114 BOOST_CONCEPT_ASSERT((DFSVisitorConcept<DFSVisitor, IncidenceGraph>));
115 typedef typename graph_traits<IncidenceGraph>::vertex_descriptor Vertex;
116 typedef typename graph_traits<IncidenceGraph>::edge_descriptor Edge;
117 BOOST_CONCEPT_ASSERT((ReadWritePropertyMapConcept<ColorMap, Vertex>));
118 typedef typename property_traits<ColorMap>::value_type ColorValue;
119 BOOST_CONCEPT_ASSERT((ColorValueConcept<ColorValue>));
120 typedef color_traits<ColorValue> Color;
121 typedef typename graph_traits<IncidenceGraph>::out_edge_iterator Iter;
122 typedef std::pair<Vertex, std::pair<boost::optional<Edge>, std::pair<Iter, Iter>>>
125 boost::optional<Edge> src_e;
130 stack.reserve(num_vertices(g));
132 put(color, u, Color::gray());
133 vis.discover_vertex(u, g);
134 boost::tie(ei, ei_end) = out_edges(u, g);
138 stack.push_back(std::make_pair(
139 u, std::make_pair(boost::optional<Edge>(), std::make_pair(ei_end, ei_end))));
143 stack.push_back(std::make_pair(
144 u, std::make_pair(boost::optional<Edge>(), std::make_pair(ei, ei_end))));
146 while(!stack.empty())
148 VertexInfo& back = stack.back();
150 src_e = back.second.first;
151 boost::tie(ei, ei_end) = back.second.second;
157 call_finish_edge(vis, src_e.get(), g);
161 Vertex v = target(*ei, g);
162 vis.examine_edge(*ei, g);
163 ColorValue v_color = get(color, v);
164 if(v_color == Color::white())
166 vis.tree_edge(*ei, g);
169 std::make_pair(u, std::make_pair(src_e, std::make_pair(++ei, ei_end))));
171 put(color, u, Color::gray());
172 vis.discover_vertex(u, g);
173 boost::tie(ei, ei_end) = out_edges(u, g);
181 if(v_color == Color::gray())
183 vis.back_edge(*ei, g);
187 vis.forward_or_cross_edge(*ei, g);
189 call_finish_edge(vis, *ei, g);
193 put(color, u, Color::black());
194 vis.finish_vertex(u, g);
199 template <
class VertexListGraph,
class DFSVisitor,
class ColorMap>
200 void custom_depth_first_search(
201 const VertexListGraph& g, DFSVisitor vis, ColorMap color,
202 typename graph_traits<VertexListGraph>::vertex_descriptor start_vertex,
203 std::vector<boost::detail::DFSVertexInfo<VertexListGraph>>& stack)
205 typedef typename graph_traits<VertexListGraph>::vertex_descriptor Vertex;
206 BOOST_CONCEPT_ASSERT((DFSVisitorConcept<DFSVisitor, VertexListGraph>));
207 typedef typename property_traits<ColorMap>::value_type ColorValue;
208 typedef color_traits<ColorValue> Color;
210 typename graph_traits<VertexListGraph>::vertex_iterator ui, ui_end;
211 for(boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui)
213 Vertex u = implicit_cast<Vertex>(*ui);
214 put(color, u, Color::white());
215 vis.initialize_vertex(u, g);
218 if(start_vertex != detail::get_default_starting_vertex(g))
220 vis.start_vertex(start_vertex, g);
221 detail::custom_depth_first_visit_impl(g, start_vertex, vis, color, stack);
224 for(boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui)
226 Vertex u = implicit_cast<Vertex>(*ui);
227 ColorValue u_color = get(color, u);
228 if(u_color == Color::white())
230 vis.start_vertex(u, g);
231 detail::custom_depth_first_visit_impl(g, u, vis, color, stack);
239 inline void remove_vertex(
typename graph_t::vertex_descriptor v, graph_t& g)
241 typedef typename graph_t::directed_category Cat;
242 g.removing_vertex(v, boost::graph_detail::iterator_stability(g.m_vertices));
243 boost::detail::remove_vertex_dispatch(g, v, Cat());
246 template <
typename VertexListGraph,
typename OutputIterator>
247 void custom_topological_sort(
248 VertexListGraph& g, OutputIterator result,
249 std::vector<boost::default_color_type>& color_map,
250 std::vector<boost::detail::DFSVertexInfo<VertexListGraph>>& stack)
253 color_map.resize(boost::num_vertices(g));
255 auto map = boost::make_iterator_property_map(
256 color_map.begin(), boost::get(boost::vertex_index, g), color_map[0]);
258 boost::custom_depth_first_search(
259 g, boost::topo_sort_visitor<OutputIterator>(result), map,
260 boost::detail::get_default_starting_vertex(g), stack);
265 using graph_vertex_t = graph_t::vertex_descriptor;
266 using graph_edge_t = graph_t::edge_descriptor;
268 #if !defined(OSSIA_NO_FAST_CONTAINERS)
269 template <
typename T,
typename V>
270 using dense_shared_ptr_map = ankerl::unordered_dense::map<
271 std::shared_ptr<T>, V, egur_hash, pointer_equal,
272 #if defined(OSSIA_SMALL_VECTOR_ALLOCATOR_REBIND_FAILS)
273 std::vector<std::pair<std::shared_ptr<T>, V>>
275 ossia::small_vector<std::pair<std::shared_ptr<T>, V>, 1024>
279 template <
typename T,
typename V>
280 using dense_shared_ptr_map
281 = ossia::hash_map<std::shared_ptr<T>, V, egur_hash, pointer_equal>;
283 using node_map = ossia::dense_shared_ptr_map<ossia::graph_node, graph_vertex_t>;
284 using edge_map = ossia::dense_shared_ptr_map<ossia::graph_edge, graph_edge_t>;
286 using node_flat_set = ossia::flat_set<graph_node*>;
287 enum class node_ordering
294 template <
typename T>
295 auto apply_con(
const T& visitor, ossia::connection& con)
297 auto tgt = con.target();
298 switch(con.which().index())
300 case ossia::connection::index_of<immediate_glutton_connection>().index():
301 return visitor(*
reinterpret_cast<immediate_glutton_connection*
>(tgt));
303 case ossia::connection::index_of<immediate_strict_connection>().index():
304 return visitor(*
reinterpret_cast<immediate_strict_connection*
>(tgt));
306 case ossia::connection::index_of<delayed_glutton_connection>().index():
307 return visitor(*
reinterpret_cast<delayed_glutton_connection*
>(tgt));
309 case ossia::connection::index_of<delayed_strict_connection>().index():
310 return visitor(*
reinterpret_cast<delayed_strict_connection*
>(tgt));
312 case ossia::connection::index_of<dependency_connection>().index():
313 return visitor(*
reinterpret_cast<dependency_connection*
>(tgt));
320 template <
typename T>
321 auto apply_con(
const T& visitor,
const ossia::connection& con)
323 auto tgt = con.target();
324 switch(con.which().index())
326 case ossia::connection::index_of<immediate_glutton_connection>().index():
327 return visitor(*
reinterpret_cast<const immediate_glutton_connection*
>(tgt));
329 case ossia::connection::index_of<immediate_strict_connection>().index():
330 return visitor(*
reinterpret_cast<const immediate_strict_connection*
>(tgt));
332 case ossia::connection::index_of<delayed_glutton_connection>().index():
333 return visitor(*
reinterpret_cast<const delayed_glutton_connection*
>(tgt));
335 case ossia::connection::index_of<delayed_strict_connection>().index():
336 return visitor(*
reinterpret_cast<const delayed_strict_connection*
>(tgt));
338 case ossia::connection::index_of<dependency_connection>().index():
339 return visitor(*
reinterpret_cast<const dependency_connection*
>(tgt));
347 template <
typename Graph_T,
typename IO>
348 void print_graph(Graph_T& g, IO& stream)
352 boost::write_graphviz(
354 [&](
auto& out,
auto v) {
355 if(g[v] && !g[v]->label().empty())
356 out <<
"[label=\"" << g[v]->label() <<
"\"]";
362 stream << s.str() <<
"\n";
366 struct OSSIA_EXPORT graph_util
368 static void pull_from_parameter(inlet& in, execution_state& e)
370 apply_to_destination(
371 in.address, e.exec_devices(),
373 if(in.scope & port::scope_t::local)
375 e.find_and_copy(*addr, in);
377 else if(in.scope & port::scope_t::global)
379 e.copy_from_global(*addr, in);
386 static void init_outlet(outlet& out, execution_state&)
388 out.visit(clear_data{});
393 static void init_inlet(inlet& in, execution_state& e)
395 bool must_copy = in.sources.empty();
397 for(
const graph_edge* edge : in.sources)
399 must_copy |= ossia::apply_con(init_must_copy_visitor{*edge}, edge->con);
403 pull_from_parameter(in, e);
405 for(
auto edge : in.sources)
407 ossia::apply_con(init_node_visitor{in, *edge, e}, edge->con);
413 static void init_node(graph_node& n, execution_state& e)
416 for_each_outlet(n, [&](
auto& port) { init_outlet(port, e); });
419 for_each_inlet(n, [&](
auto& port) { init_inlet(port, e); });
423 static void teardown_outlet(outlet& out, execution_state& e)
426 bool must_copy = out.targets.empty();
430 for(
const auto& tgt : out.targets)
432 must_copy |= ossia::apply_con(env_writer{out, *tgt}, tgt->con);
442 static void teardown_inlet(inlet& in, execution_state&)
445 in.visit(clear_data{});
448 static void teardown_node(
const graph_node& n, execution_state& e)
452 for_each_outlet(n, [&](
auto& port) { teardown_outlet(port, e); });
455 for_each_inlet(n, [&](
auto& port) { teardown_inlet(port, e); });
475 static bool disable_strict_nodes(
const graph_node* node)
480 auto test_disable_inlet = [&](
const ossia::inlet& inlet) {
481 for(
const auto& edge : inlet.sources)
484 assert(edge->out_node);
486 if(immediate_strict_connection* sc
487 = edge->con.target<immediate_strict_connection>())
489 if((sc->required_sides
490 & immediate_strict_connection::required_sides_t::outbound)
491 && node->enabled() && !edge->out_node->enabled())
497 delayed_strict_connection* delay
498 = edge->con.target<delayed_strict_connection>())
500 const auto n = ossia::apply(data_size{}, delay->buffer);
501 if(n == 0 || delay->pos >= n)
510 auto test_disable_outlet = [&](
const ossia::outlet& outlet) {
511 for(
const auto& edge : outlet.targets)
513 assert(edge->in_node);
515 if(
auto sc = edge->con.target<immediate_strict_connection>())
517 if((sc->required_sides
518 & immediate_strict_connection::required_sides_t::inbound)
519 && node->enabled() && !edge->in_node->enabled())
528 if(any_of_inlet(*node, test_disable_inlet))
530 if(any_of_outlet(*node, test_disable_outlet))
537 disable_strict_nodes(
const node_flat_set& enabled_nodes, node_flat_set& ret)
539 for(graph_node* node : enabled_nodes)
541 if(disable_strict_nodes(node))
546 static void disable_strict_nodes_rec(
547 node_flat_set& cur_enabled_node, node_flat_set& disabled_cache)
551 disabled_cache.clear();
552 disable_strict_nodes(cur_enabled_node, disabled_cache);
553 for(ossia::graph_node* n : disabled_cache)
557 cur_enabled_node.erase(n);
560 }
while(!disabled_cache.empty());
563 static void log_inputs(
const graph_node&, ossia::logger_type&
logger);
564 static void log_outputs(
const graph_node&, ossia::logger_type&
logger);
566 static void run_scaled(graph_node& first_node, execution_state& e);
568 static void exec_node(graph_node& first_node, execution_state& e)
570 init_node(first_node, e);
571 if(!first_node.requested_tokens.empty())
573 if(first_node.start_discontinuous())
575 first_node.requested_tokens.front().start_discontinuous =
true;
576 first_node.set_start_discontinuous(
false);
578 if(first_node.end_discontinuous())
580 first_node.requested_tokens.front().end_discontinuous =
true;
581 first_node.set_end_discontinuous(
false);
585 for(
const auto& request : first_node.requested_tokens)
587 first_node.run(request, {&e});
590 first_node.set_executed(
true);
591 teardown_node(first_node, e);
595 exec_node(graph_node& first_node, execution_state& e, ossia::logger_type&
logger)
597 init_node(first_node, e);
598 if(first_node.start_discontinuous())
600 first_node.requested_tokens.front().start_discontinuous =
true;
601 first_node.set_start_discontinuous(
false);
603 if(first_node.end_discontinuous())
605 first_node.requested_tokens.front().end_discontinuous =
true;
606 first_node.set_end_discontinuous(
false);
609 log_inputs(first_node,
logger);
610 for(
const auto& request : first_node.requested_tokens)
612 first_node.run(request, {&e});
614 log_outputs(first_node,
logger);
616 first_node.set_executed(
true);
617 teardown_node(first_node, e);
621 static bool can_execute(graph_node& node,
const execution_state&)
623 return all_of_inlet(node, [](
const auto& inlet) {
629 bool previous_nodes_executed = ossia::all_of(inlet.sources, [&](graph_edge* edge) {
630 return edge->out_node->executed()
631 || (!edge->out_node->enabled()
637 return !inlet.sources.empty() ? previous_nodes_executed :
true;
641 static void finish_nodes(
const node_map& nodes)
643 for(
auto& node : nodes)
645 ossia::graph_node& n = *node.first;
646 n.set_executed(
false);
651 template <
typename DevicesT>
652 static auto find_address_connection(
653 ossia::graph_node& source, ossia::graph_node& sink,
const DevicesT& devices)
655 return any_of_outlet(source, [&](
auto& outlet) {
656 return any_of_inlet(sink, [&](
auto& inlet) {
658 apply_to_destination(
659 outlet.address, devices,
661 apply_to_destination(
662 inlet.address, devices,
663 [&](ossia::net::parameter_base* p2, bool) {
667 do_nothing_for_nodes{});
669 do_nothing_for_nodes{});
676 struct OSSIA_EXPORT graph_base : graph_interface
678 graph_base() noexcept
680 m_nodes.reserve(1024);
681 m_node_list.reserve(1024);
682 m_edges.reserve(1024);
684 [[nodiscard]] tcb::span<ossia::graph_node* const>
685 get_nodes() const noexcept final
override
687 return tcb::span{m_node_list};
690 void recompute_maps()
695 auto vertices = boost::vertices(m_graph);
696 m_nodes.reserve(m_graph.m_vertices.size());
697 for(
auto it = vertices.first; it != vertices.second; ++it)
699 graph_vertex_t k = *it;
700 node_ptr n = m_graph[k];
703 m_nodes.insert({n, k});
707 #if !defined(__clang__)
708 #pragma GCC diagnostic push
709 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
711 auto edges = boost::edges(m_graph);
712 m_edges.reserve(std::distance(edges.first, edges.second));
713 for(
auto it = edges.first; it != edges.second; ++it)
715 graph_edge_t k = *it;
716 edge_ptr n = m_graph[k];
719 m_edges.insert({n, k});
721 #if !defined(__clang__)
722 #pragma GCC diagnostic pop
726 auto add_node_impl(node_ptr n)
731 auto vtx = boost::add_vertex(n, m_graph);
733 m_node_list.push_back(n.get());
739 void add_node(node_ptr n)
final override
741 if(m_nodes.find(n) == m_nodes.end())
743 add_node_impl(std::move(n));
747 void remove_node(
const node_ptr& n)
final override
749 for_each_inlet(*n, [&](
auto& port) {
750 auto s = port.sources;
754 for_each_outlet(*n, [&](
auto& port) {
755 auto s = port.targets;
760 auto it = m_nodes.find(n);
761 if(it != m_nodes.end())
763 auto vtx = boost::vertices(m_graph);
764 if(std::find(vtx.first, vtx.second, it->second) != vtx.second)
766 boost::clear_vertex(it->second, m_graph);
767 remove_vertex(it->second, m_graph);
773 ossia::remove_one(m_node_list, n.get());
777 void connect(std::shared_ptr<graph_edge> edge)
final override
783 graph_vertex_t in_vtx, out_vtx;
784 auto it1 = m_nodes.find(edge->in_node);
785 if(it1 == m_nodes.end())
786 in_vtx = add_node_impl(edge->in_node);
788 in_vtx = it1->second;
790 auto it2 = m_nodes.find(edge->out_node);
791 if(it2 == m_nodes.end())
792 out_vtx = add_node_impl(edge->out_node);
794 out_vtx = it2->second;
797 boost::add_edge(in_vtx, out_vtx, edge, m_graph);
803 void disconnect(
const std::shared_ptr<graph_edge>& edge)
final override
805 disconnect(edge.get());
808 void disconnect(graph_edge* edge)
final override
813 auto it = m_edges.find(edge);
814 if(it != m_edges.end())
816 auto edg = boost::edges(m_graph);
817 if(std::find(edg.first, edg.second, it->second) != edg.second)
819 boost::remove_edge(it->second, m_graph);
828 void clear() final
override
832 for(
auto& edge : m_edges)
836 for(
auto& node : m_nodes)
847 void mark_dirty() final
override { m_dirty =
true; }
849 ~graph_base()
override { clear(); }
853 ossia::small_vector<ossia::graph_node*, 1024> m_node_list;
The node_base class.
Definition: network/base/node.hpp:48
The parameter_base class.
Definition: ossia/network/base/parameter.hpp:48
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition: context.cpp:104