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/disable_fpe.hpp>
16#include <ossia/detail/flat_set.hpp>
17#include <ossia/detail/ptr_set.hpp>
20#include <boost/graph/adjacency_list.hpp>
21#include <boost/graph/filtered_graph.hpp>
22#include <boost/predef.h>
25#include <boost/graph/topological_sort.hpp>
27#include <ankerl/unordered_dense.h>
29#if BOOST_LIB_STD_GNU >= BOOST_VERSION_NUMBER(13, 0, 0) \
30 && BOOST_VERSION_NUMBER <= BOOST_VERSION_NUMBER(1, 83, 0)
31#define OSSIA_SMALL_VECTOR_ALLOCATOR_REBIND_FAILS 1
37using graph_t = boost::adjacency_list<
38 boost::smallvecS, boost::smallvecS, boost::directedS, node_ptr,
39 std::shared_ptr<graph_edge>>;
41namespace boost::detail
45class stored_edge_property<unsigned long, std::shared_ptr<ossia::graph_edge>>
46 :
public stored_edge<unsigned long>
48 using Vertex =
unsigned long;
49 using Property = std::shared_ptr<ossia::graph_edge>;
50 typedef stored_edge_property self;
51 typedef stored_edge<Vertex> Base;
54 typedef Property property_type;
55 inline stored_edge_property() { }
56 inline stored_edge_property(Vertex target, Property p = {})
57 : stored_edge<Vertex>(target)
58 , m_property(std::move(p))
61 stored_edge_property(self&& x) noexcept
62 : Base(
static_cast<Base&&
>(x))
63 , m_property(std::move(x.m_property))
66 stored_edge_property(self
const& x)
67 : Base(static_cast<Base const&>(x))
68 , m_property(std::move(const_cast<self&>(x).m_property))
71 self& operator=(self&& x)
75 static_cast<Base&
>(*this) =
static_cast<Base&&
>(x);
76 m_property = std::move(x.m_property);
79 self& operator=(self
const& x)
83 static_cast<Base&
>(*this) =
static_cast<Base const&
>(x);
84 m_property = std::move(
const_cast<self&
>(x).m_property);
87 inline Property& get_property() noexcept {
return m_property; }
88 inline const Property& get_property() const noexcept {
return m_property; }
102using VertexDescriptor =
typename graph_traits<G>::vertex_descriptor;
103template <
typename G,
typename FilteredG>
104using OutEdgeIterator = std::decay_t<decltype(std::get<0>(
105 out_edges(std::declval<VertexDescriptor<G>>(), std::declval<FilteredG>())))>;
107template <
typename G,
typename FilteredG>
108using DFSVertexInfo = std::pair<
109 typename graph_traits<G>::vertex_descriptor,
111 boost::optional<typename graph_traits<G>::edge_descriptor>,
112 std::pair<OutEdgeIterator<G, FilteredG>, OutEdgeIterator<G, FilteredG>>>>;
115 typename IncidenceGraph,
typename FilteredGraph,
class DFSVisitor,
class ColorMap>
116void custom_depth_first_visit_impl(
117 const FilteredGraph& g,
typename graph_traits<IncidenceGraph>::vertex_descriptor u,
118 DFSVisitor& vis, ColorMap color,
119 std::vector<DFSVertexInfo<IncidenceGraph, FilteredGraph>>& stack)
121 constexpr detail::nontruth2 func;
122 BOOST_CONCEPT_ASSERT((IncidenceGraphConcept<IncidenceGraph>));
123 BOOST_CONCEPT_ASSERT((DFSVisitorConcept<DFSVisitor, IncidenceGraph>));
124 typedef typename graph_traits<IncidenceGraph>::vertex_descriptor Vertex;
125 typedef typename graph_traits<IncidenceGraph>::edge_descriptor Edge;
126 BOOST_CONCEPT_ASSERT((ReadWritePropertyMapConcept<ColorMap, Vertex>));
127 typedef typename property_traits<ColorMap>::value_type ColorValue;
128 BOOST_CONCEPT_ASSERT((ColorValueConcept<ColorValue>));
129 typedef color_traits<ColorValue> Color;
130 typedef typename graph_traits<IncidenceGraph>::out_edge_iterator Iter;
131 typedef std::pair<Vertex, std::pair<boost::optional<Edge>, std::pair<Iter, Iter>>>
134 boost::optional<Edge> src_e;
138 stack.reserve(num_vertices(g));
140 put(color, u, Color::gray());
141 vis.discover_vertex(u, g);
142 auto [ei, ei_end] = out_edges(u, g);
147 stack.push_back(std::make_pair(
148 u, std::make_pair(boost::optional<Edge>(), std::make_pair(ei_end, ei_end))));
152 stack.push_back(std::make_pair(
153 u, std::make_pair(boost::optional<Edge>(), std::make_pair(ei, ei_end))));
155 while(!stack.empty())
157 auto& back = stack.back();
159 src_e = back.second.first;
160 boost::tie(ei, ei_end) = back.second.second;
166 call_finish_edge(vis, src_e.get(), g);
170 Vertex v = target(*ei, g);
171 vis.examine_edge(*ei, g);
172 ColorValue v_color = get(color, v);
173 if(v_color == Color::white())
175 vis.tree_edge(*ei, g);
178 std::make_pair(u, std::make_pair(src_e, std::make_pair(++ei, ei_end))));
180 put(color, u, Color::gray());
181 vis.discover_vertex(u, g);
182 boost::tie(ei, ei_end) = out_edges(u, g);
190 if(v_color == Color::gray())
192 vis.back_edge(*ei, g);
196 vis.forward_or_cross_edge(*ei, g);
198 call_finish_edge(vis, *ei, g);
202 put(color, u, Color::black());
203 vis.finish_vertex(u, g);
209 typename VertexListGraph,
typename FilteredGraph,
class DFSVisitor,
class ColorMap>
210void custom_depth_first_search(
211 const FilteredGraph& g, DFSVisitor vis, ColorMap color,
212 typename graph_traits<VertexListGraph>::vertex_descriptor start_vertex,
213 std::vector<boost::detail::DFSVertexInfo<VertexListGraph, FilteredGraph>>& stack)
215 typedef typename graph_traits<VertexListGraph>::vertex_descriptor Vertex;
216 BOOST_CONCEPT_ASSERT((DFSVisitorConcept<DFSVisitor, VertexListGraph>));
217 typedef typename property_traits<ColorMap>::value_type ColorValue;
218 typedef color_traits<ColorValue> Color;
220 for(
auto [ui, ui_end] = vertices(g); ui != ui_end; ++ui)
222 Vertex u = implicit_cast<Vertex>(*ui);
223 put(color, u, Color::white());
224 vis.initialize_vertex(u, g);
227 if(start_vertex != detail::get_default_starting_vertex(g))
229 vis.start_vertex(start_vertex, g);
230 detail::custom_depth_first_visit_impl<VertexListGraph>(
231 g, start_vertex, vis, color, stack);
234 for(
auto [ui, ui_end] = vertices(g); ui != ui_end; ++ui)
236 Vertex u = implicit_cast<Vertex>(*ui);
237 ColorValue u_color = get(color, u);
238 if(u_color == Color::white())
240 vis.start_vertex(u, g);
241 detail::custom_depth_first_visit_impl<VertexListGraph>(g, u, vis, color, stack);
249inline void remove_vertex(
typename graph_t::vertex_descriptor v, graph_t& g)
251 typedef typename graph_t::directed_category Cat;
252 g.removing_vertex(v, boost::graph_detail::iterator_stability(g.m_vertices));
253 boost::detail::remove_vertex_dispatch(g, v, Cat());
256template <
typename VertexListGraph,
typename FilteredGraph,
typename OutputIterator>
257void custom_topological_sort(
258 FilteredGraph& g, OutputIterator result,
259 std::vector<boost::default_color_type>& color_map,
260 std::vector<boost::detail::DFSVertexInfo<VertexListGraph, FilteredGraph>>& stack)
263 color_map.resize(boost::num_vertices(g));
265 auto map = boost::make_iterator_property_map(
266 color_map.begin(), boost::get(boost::vertex_index, g), color_map[0]);
268 boost::custom_depth_first_search<VertexListGraph>(
269 g, boost::topo_sort_visitor<OutputIterator>(result), map,
270 boost::detail::get_default_starting_vertex(g), stack);
275using graph_vertex_t = graph_t::vertex_descriptor;
276using graph_edge_t = graph_t::edge_descriptor;
278#if !defined(OSSIA_NO_FAST_CONTAINERS)
279template <
typename T,
typename V>
280using dense_shared_ptr_map = ankerl::unordered_dense::map<
281 std::shared_ptr<T>, V, egur_hash, pointer_equal,
282#if defined(OSSIA_SMALL_VECTOR_ALLOCATOR_REBIND_FAILS)
283 std::vector<std::pair<std::shared_ptr<T>, V>>
285 ossia::small_vector<std::pair<std::shared_ptr<T>, V>, 1024>
289template <
typename T,
typename V>
290using dense_shared_ptr_map
291 = ossia::hash_map<std::shared_ptr<T>, V, egur_hash, pointer_equal>;
293using node_map = ossia::dense_shared_ptr_map<ossia::graph_node, graph_vertex_t>;
294using edge_map = ossia::dense_shared_ptr_map<ossia::graph_edge, graph_edge_t>;
296using node_flat_set = ossia::flat_set<graph_node*>;
297enum class node_ordering
305auto apply_con(
const T& visitor, ossia::connection& con)
307 auto tgt = con.target();
308 switch(con.which().index())
310 case ossia::connection::index_of<immediate_glutton_connection>().index():
311 return visitor(*
reinterpret_cast<immediate_glutton_connection*
>(tgt));
313 case ossia::connection::index_of<immediate_strict_connection>().index():
314 return visitor(*
reinterpret_cast<immediate_strict_connection*
>(tgt));
316 case ossia::connection::index_of<delayed_glutton_connection>().index():
317 return visitor(*
reinterpret_cast<delayed_glutton_connection*
>(tgt));
319 case ossia::connection::index_of<delayed_strict_connection>().index():
320 return visitor(*
reinterpret_cast<delayed_strict_connection*
>(tgt));
322 case ossia::connection::index_of<dependency_connection>().index():
323 return visitor(*
reinterpret_cast<dependency_connection*
>(tgt));
331auto apply_con(
const T& visitor,
const ossia::connection& con)
333 auto tgt = con.target();
334 switch(con.which().index())
336 case ossia::connection::index_of<immediate_glutton_connection>().index():
337 return visitor(*
reinterpret_cast<const immediate_glutton_connection*
>(tgt));
339 case ossia::connection::index_of<immediate_strict_connection>().index():
340 return visitor(*
reinterpret_cast<const immediate_strict_connection*
>(tgt));
342 case ossia::connection::index_of<delayed_glutton_connection>().index():
343 return visitor(*
reinterpret_cast<const delayed_glutton_connection*
>(tgt));
345 case ossia::connection::index_of<delayed_strict_connection>().index():
346 return visitor(*
reinterpret_cast<const delayed_strict_connection*
>(tgt));
348 case ossia::connection::index_of<dependency_connection>().index():
349 return visitor(*
reinterpret_cast<const dependency_connection*
>(tgt));
357template <
typename Graph_T,
typename IO>
358void print_graph(Graph_T& g, IO& stream)
362 boost::write_graphviz(
364 [&](
auto& out,
auto v) {
365 if(g[v] && !g[v]->label().empty())
366 out <<
"[label=\"" << g[v]->label() <<
"\"]";
372 stream << s.str() <<
"\n";
380 bool operator()(
const boost::graph_traits<graph_t>::edge_descriptor& e)
const
382 return !(*g)[e]->delayed();
386struct OSSIA_EXPORT graph_util
388 static void pull_from_parameter(inlet& in, execution_state& e)
390 apply_to_destination(
391 in.address, e.exec_devices(),
393 if(in.scope & port::scope_t::local)
395 e.find_and_copy(*addr, in);
397 else if(in.scope & port::scope_t::global)
399 e.copy_from_global(*addr, in);
406 static void init_outlet(outlet& out, execution_state&)
408 out.visit(clear_data{});
413 static void init_inlet(inlet& in, execution_state& e)
415 bool must_copy = in.sources.empty();
417 for(
const graph_edge* edge : in.sources)
419 must_copy |= ossia::apply_con(init_must_copy_visitor{*edge}, edge->con);
423 pull_from_parameter(in, e);
425 for(
auto edge : in.sources)
427 ossia::apply_con(init_node_visitor{in, *edge, e}, edge->con);
433 static void init_node(graph_node& n, execution_state& e)
442 for_each_outlet(n, [&](
auto& port) { init_outlet(port, e); });
445 for_each_inlet(n, [&](
auto& port) { init_inlet(port, e); });
449 static void teardown_outlet(outlet& out, execution_state& e)
452 bool must_copy = out.targets.empty();
456 for(
const auto& tgt : out.targets)
458 must_copy |= ossia::apply_con(env_writer{out, *tgt}, tgt->con);
468 static void teardown_inlet(inlet& in, execution_state&)
471 in.visit(clear_data{});
474 static void teardown_node(
const graph_node& n, execution_state& e)
484 for_each_outlet(n, [&](
auto& port) { teardown_outlet(port, e); });
487 for_each_inlet(n, [&](
auto& port) { teardown_inlet(port, e); });
507 static bool disable_strict_nodes(
const graph_node* node)
512 auto test_disable_inlet = [&](
const ossia::inlet& inlet) {
513 for(
const auto& edge : inlet.sources)
516 assert(edge->out_node);
518 if(immediate_strict_connection* sc
519 = edge->con.target<immediate_strict_connection>())
521 if((sc->required_sides
522 & immediate_strict_connection::required_sides_t::outbound)
523 && node->enabled() && !edge->out_node->enabled())
529 delayed_strict_connection* delay
530 = edge->con.target<delayed_strict_connection>())
532 const auto n = ossia::apply(data_size{}, delay->buffer);
533 if(n == 0 || delay->pos >= n)
542 auto test_disable_outlet = [&](
const ossia::outlet& outlet) {
543 for(
const auto& edge : outlet.targets)
545 assert(edge->in_node);
547 if(
auto sc = edge->con.target<immediate_strict_connection>())
549 if((sc->required_sides
550 & immediate_strict_connection::required_sides_t::inbound)
551 && node->enabled() && !edge->in_node->enabled())
560 if(any_of_inlet(*node, test_disable_inlet))
562 if(any_of_outlet(*node, test_disable_outlet))
569 disable_strict_nodes(
const node_flat_set& enabled_nodes, node_flat_set& ret)
571 for(graph_node* node : enabled_nodes)
573 if(disable_strict_nodes(node))
578 static void disable_strict_nodes_rec(
579 node_flat_set& cur_enabled_node, node_flat_set& disabled_cache)
583 disabled_cache.clear();
584 disable_strict_nodes(cur_enabled_node, disabled_cache);
585 for(ossia::graph_node* n : disabled_cache)
589 cur_enabled_node.erase(n);
592 }
while(!disabled_cache.empty());
595 static void log_inputs(
const graph_node&, ossia::logger_type&
logger);
596 static void log_outputs(
const graph_node&, ossia::logger_type&
logger);
598 static void check_inputs(
const graph_node&, execution_state& e);
600 check_outputs(
const graph_node&, execution_state& e,
const ossia::token_request& req);
601 static void run_scaled(graph_node& first_node, execution_state& e);
604#define OSSIA_DEBUG_MISBEHAVING_NODES 1
606 static void exec_node(graph_node& first_node, execution_state& e)
608 init_node(first_node, e);
610#if defined(OSSIA_DEBUG_MISBEHAVING_NODES)
611 check_inputs(first_node, e);
614 if(!first_node.requested_tokens.empty())
616 if(first_node.start_discontinuous())
618 first_node.requested_tokens.front().start_discontinuous =
true;
619 first_node.set_start_discontinuous(
false);
621 if(first_node.end_discontinuous())
623 first_node.requested_tokens.front().end_discontinuous =
true;
624 first_node.set_end_discontinuous(
false);
628 for(
const auto& request : first_node.requested_tokens)
630 first_node.run(request, {&e});
631 first_node.process_time(request, e);
632#if defined(OSSIA_DEBUG_MISBEHAVING_NODES)
633 check_outputs(first_node, e, request);
637 first_node.set_executed(
true);
638 teardown_node(first_node, e);
642 exec_node(graph_node& first_node, execution_state& e, ossia::logger_type&
logger)
644 init_node(first_node, e);
646#if defined(OSSIA_DEBUG_MISBEHAVING_NODES)
647 check_inputs(first_node, e);
650 if(!first_node.requested_tokens.empty())
652 if(first_node.start_discontinuous())
654 first_node.requested_tokens.front().start_discontinuous =
true;
655 first_node.set_start_discontinuous(
false);
657 if(first_node.end_discontinuous())
659 first_node.requested_tokens.front().end_discontinuous =
true;
660 first_node.set_end_discontinuous(
false);
664 log_inputs(first_node,
logger);
665 for(
const auto& request : first_node.requested_tokens)
667 first_node.run(request, {&e});
668 first_node.process_time(request, e);
669#if defined(OSSIA_DEBUG_MISBEHAVING_NODES)
670 check_outputs(first_node, e, request);
673 log_outputs(first_node,
logger);
675 first_node.set_executed(
true);
676 teardown_node(first_node, e);
680 static bool can_execute(graph_node& node,
const execution_state&)
682 return all_of_inlet(node, [](
const auto& inlet) {
688 bool previous_nodes_executed = ossia::all_of(inlet.sources, [&](graph_edge* edge) {
689 return edge->out_node->executed()
690 || (!edge->out_node->enabled()
697 return !inlet.sources.empty() ? previous_nodes_executed :
true;
701 static void finish_nodes(
const node_map& nodes)
703 for(
auto& node : nodes)
705 ossia::graph_node& n = *node.first;
706 n.set_executed(
false);
711 template <
typename DevicesT>
712 static auto find_address_connection(
713 ossia::graph_node& source, ossia::graph_node& sink,
const DevicesT& devices)
715 return any_of_outlet(source, [&](
auto& outlet) {
716 return any_of_inlet(sink, [&](
auto& inlet) {
718 apply_to_destination(
719 outlet.address, devices,
721 apply_to_destination(
722 inlet.address, devices,
723 [&](ossia::net::parameter_base* p2, bool) {
727 do_nothing_for_nodes{});
729 do_nothing_for_nodes{});
736struct OSSIA_EXPORT graph_base : graph_interface
738 graph_base() noexcept
740#if !defined(OSSIA_FREESTANDING)
741 m_nodes.reserve(1024);
742 m_node_list.reserve(1024);
743 m_edges.reserve(1024);
746 [[nodiscard]] std::span<ossia::graph_node* const>
747 get_nodes() const noexcept final
override
749 return std::span{m_node_list};
752 void recompute_maps()
757 auto vertices = boost::vertices(m_graph);
758 m_nodes.reserve(m_graph.m_vertices.size());
759 for(
auto it = vertices.first; it != vertices.second; ++it)
761 graph_vertex_t k = *it;
762 node_ptr n = m_graph[k];
765 m_nodes.insert({n, k});
769#if !defined(__clang__)
770#pragma GCC diagnostic push
771#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
773 auto edges = boost::edges(m_graph);
774 m_edges.reserve(std::distance(edges.first, edges.second));
775 for(
auto it = edges.first; it != edges.second; ++it)
777 graph_edge_t k = *it;
778 edge_ptr n = m_graph[k];
781 m_edges.insert({n, k});
783#if !defined(__clang__)
784#pragma GCC diagnostic pop
788 auto add_node_impl(node_ptr n)
793 auto vtx = boost::add_vertex(n, m_graph);
795 m_node_list.push_back(n.get());
801 void add_node(node_ptr n)
final override
803 if(m_nodes.find(n) == m_nodes.end())
805 add_node_impl(std::move(n));
809 void remove_node(
const node_ptr& n)
final override
811 for_each_inlet(*n, [&](
auto& port) {
812 auto s = port.sources;
816 for_each_outlet(*n, [&](
auto& port) {
817 auto s = port.targets;
822 auto it = m_nodes.find(n);
823 if(it != m_nodes.end())
825 auto vtx = boost::vertices(m_graph);
826 if(std::find(vtx.first, vtx.second, it->second) != vtx.second)
828 boost::clear_vertex(it->second, m_graph);
829 remove_vertex(it->second, m_graph);
835 ossia::remove_one(m_node_list, n.get());
839 void connect(std::shared_ptr<graph_edge> edge)
final override
845 graph_vertex_t in_vtx, out_vtx;
846 auto it1 = m_nodes.find(edge->in_node);
847 if(it1 == m_nodes.end())
848 in_vtx = add_node_impl(edge->in_node);
850 in_vtx = it1->second;
852 auto it2 = m_nodes.find(edge->out_node);
853 if(it2 == m_nodes.end())
854 out_vtx = add_node_impl(edge->out_node);
856 out_vtx = it2->second;
859 boost::add_edge(in_vtx, out_vtx, edge, m_graph);
865 void disconnect(
const std::shared_ptr<graph_edge>& edge)
final override
867 disconnect(edge.get());
870 void disconnect(graph_edge* edge)
final override
875 auto it = m_edges.find(edge);
876 if(it != m_edges.end())
878 auto edg = boost::edges(m_graph);
879 if(std::find(edg.first, edg.second, it->second) != edg.second)
881 boost::remove_edge(it->second, m_graph);
890 void clear() final
override
894 for(
auto& edge : m_edges)
898 for(
auto& node : m_nodes)
909 void mark_dirty() final
override { m_dirty =
true; }
911 ~graph_base()
override { clear(); }
915#if defined(OSSIA_FREESTANDING)
916 ossia::small_vector<ossia::graph_node*, 16> m_node_list;
918 ossia::small_vector<ossia::graph_node*, 1024> m_node_list;
The node_base class.
Definition 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:118