Kokkos Core Kernels Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Kokkos_GraphNode.hpp
1 //@HEADER
2 // ************************************************************************
3 //
4 // Kokkos v. 4.0
5 // Copyright (2022) National Technology & Engineering
6 // Solutions of Sandia, LLC (NTESS).
7 //
8 // Under the terms of Contract DE-NA0003525 with NTESS,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12 // See https://kokkos.org/LICENSE for license information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //@HEADER
16 
17 #ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
18 #include <Kokkos_Macros.hpp>
19 static_assert(false,
20  "Including non-public Kokkos header files is not allowed.");
21 #endif
22 #ifndef KOKKOS_KOKKOS_GRAPHNODE_HPP
23 #define KOKKOS_KOKKOS_GRAPHNODE_HPP
24 
25 #include <Kokkos_Macros.hpp>
26 
27 #include <impl/Kokkos_Error.hpp> // contract macros
28 
29 #include <Kokkos_Core_fwd.hpp>
30 #include <Kokkos_Graph_fwd.hpp>
31 #include <impl/Kokkos_GraphImpl_fwd.hpp>
32 #include <Kokkos_Parallel_Reduce.hpp>
33 #include <impl/Kokkos_GraphImpl_Utilities.hpp>
34 #include <impl/Kokkos_GraphImpl.hpp> // GraphAccess
35 
36 #include <memory> // std::shared_ptr
37 
38 namespace Kokkos {
39 namespace Experimental {
40 
41 template <class ExecutionSpace, class Kernel /*= TypeErasedTag*/,
42  class Predecessor /*= TypeErasedTag*/>
43 class GraphNodeRef {
44  //----------------------------------------------------------------------------
45  // <editor-fold desc="template parameter constraints"> {{{2
46 
47  // Note: because of these assertions, instantiating this class template is not
48  // intended to be SFINAE-safe, so do validation before you instantiate.
49 
50  static_assert(
51  std::is_same_v<Predecessor, TypeErasedTag> ||
52  Kokkos::Impl::is_specialization_of<Predecessor, GraphNodeRef>::value,
53  "Invalid predecessor template parameter given to GraphNodeRef");
54 
55  static_assert(
56  Kokkos::is_execution_space<ExecutionSpace>::value,
57  "Invalid execution space template parameter given to GraphNodeRef");
58 
59  static_assert(std::is_same_v<Predecessor, TypeErasedTag> ||
60  Kokkos::Impl::is_graph_kernel<Kernel>::value ||
61  Kokkos::Impl::is_graph_capture_v<Kernel> ||
62  Kokkos::Impl::is_graph_then_host_v<Kernel>,
63  "Invalid kernel template parameter given to GraphNodeRef");
64 
65  static_assert(!Kokkos::Impl::is_more_type_erased<Kernel, Predecessor>::value,
66  "The kernel of a graph node can't be more type-erased than the "
67  "predecessor");
68 
69  // </editor-fold> end template parameter constraints }}}2
70  //----------------------------------------------------------------------------
71 
72  public:
73  //----------------------------------------------------------------------------
74  // <editor-fold desc="public member types"> {{{2
75 
76  using execution_space = ExecutionSpace;
77  using graph_kernel = Kernel;
78  using graph_predecessor = Predecessor;
79 
80  // </editor-fold> end public member types }}}2
81  //----------------------------------------------------------------------------
82 
83  private:
84  //----------------------------------------------------------------------------
85  // <editor-fold desc="Friends"> {{{2
86 
87  template <class, class, class>
88  friend class GraphNodeRef;
89  friend struct Kokkos::Impl::GraphAccess;
90  friend struct Graph<execution_space>;
91 
92  // </editor-fold> end Friends }}}2
93  //----------------------------------------------------------------------------
94 
95  //----------------------------------------------------------------------------
96  // <editor-fold desc="Private Data Members"> {{{2
97 
98  using graph_impl_t = Kokkos::Impl::GraphImpl<ExecutionSpace>;
99  std::weak_ptr<graph_impl_t> m_graph_impl;
100 
101  // TODO @graphs figure out if we can get away with a weak reference here?
102  // GraphNodeRef instances shouldn't be stored by users outside
103  // of the create_graph closure, and so if we restructure things
104  // slightly, we could make it so that the graph owns the
105  // node_impl_t instance and this only holds a std::weak_ptr to
106  // it.
107  using node_impl_t =
108  Kokkos::Impl::GraphNodeImpl<ExecutionSpace, Kernel, Predecessor>;
109  std::shared_ptr<node_impl_t> m_node_impl;
110 
111  // </editor-fold> end Private Data Members }}}2
112  //----------------------------------------------------------------------------
113 
114  //----------------------------------------------------------------------------
115  // <editor-fold desc="Implementation detail accessors"> {{{2
116 
117  // Internally, use shallow constness
118  node_impl_t& get_node_impl() const { return *m_node_impl.get(); }
119  std::shared_ptr<node_impl_t> const& get_node_ptr() const& {
120  return m_node_impl;
121  }
122  std::shared_ptr<node_impl_t> get_node_ptr() && {
123  return std::move(m_node_impl);
124  }
125  std::weak_ptr<graph_impl_t> get_graph_weak_ptr() const {
126  return m_graph_impl;
127  }
128 
129  // </editor-fold> end Implementation detail accessors }}}2
130  //----------------------------------------------------------------------------
131 
132  // TODO kernel name propagation and exposure
133 
134  template <class NextKernelDeduced>
135  auto _then_kernel(NextKernelDeduced&& arg_kernel) const {
136  static_assert(Kokkos::Impl::is_graph_kernel_v<
137  Kokkos::Impl::remove_cvref_t<NextKernelDeduced>>,
138  "Kokkos internal error");
139 
140  auto graph_ptr = m_graph_impl.lock();
141  KOKKOS_EXPECTS(bool(graph_ptr))
142 
143  using next_kernel_t = Kokkos::Impl::remove_cvref_t<NextKernelDeduced>;
144 
145  using return_t = GraphNodeRef<ExecutionSpace, next_kernel_t, GraphNodeRef>;
146 
147  auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
148  m_graph_impl,
149  Kokkos::Impl::GraphAccess::make_node_shared_ptr<
150  typename return_t::node_impl_t>(
151  m_node_impl->execution_space_instance(),
152  Kokkos::Impl::_graph_node_kernel_ctor_tag{},
153  (NextKernelDeduced&&)arg_kernel,
154  // *this is the predecessor
155  Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
156 
157  // Add the node itself to the backend's graph data structure, now that
158  // everything is set up.
159  graph_ptr->add_node(rv.m_node_impl);
160  // Add the predecessor we stored in the constructor above in the backend's
161  // data structure, now that everything is set up.
162  graph_ptr->add_predecessor(rv.m_node_impl, *this);
163  KOKKOS_ENSURES(bool(rv.m_node_impl))
164  return rv;
165  }
166 
167  //----------------------------------------------------------------------------
168  // <editor-fold desc="Private constructors"> {{{2
169 
170  GraphNodeRef(std::weak_ptr<graph_impl_t> arg_graph_impl,
171  std::shared_ptr<node_impl_t> arg_node_impl)
172  : m_graph_impl(std::move(arg_graph_impl)),
173  m_node_impl(std::move(arg_node_impl)) {}
174 
175  // </editor-fold> end Private constructors }}}2
176  //----------------------------------------------------------------------------
177 
178  public:
179  //----------------------------------------------------------------------------
180  // <editor-fold desc="Constructors, destructors, and assignment"> {{{2
181 
182  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
183  // <editor-fold desc="rule of 6 ctors"> {{{3
184 
185  // Copyable and movable (basically just shared_ptr semantics
186  GraphNodeRef() noexcept = default;
187  GraphNodeRef(GraphNodeRef const&) = default;
188  GraphNodeRef(GraphNodeRef&&) noexcept = default;
189  GraphNodeRef& operator=(GraphNodeRef const&) = default;
190  GraphNodeRef& operator=(GraphNodeRef&&) noexcept = default;
191  ~GraphNodeRef() = default;
192 
193  // </editor-fold> end rule of 6 ctors }}}3
194  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195 
196  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
197  // <editor-fold desc="Type-erasing converting ctor and assignment"> {{{3
198 
199  template <class OtherKernel, class OtherPredecessor,
200  std::enable_if_t<
201  // Not a copy/move constructor
202  !std::is_same_v<GraphNodeRef,
203  GraphNodeRef<execution_space, OtherKernel,
204  OtherPredecessor>> &&
205  // must be an allowed type erasure of the kernel
206  Kokkos::Impl::is_compatible_type_erasure<
207  OtherKernel, graph_kernel>::value &&
208  // must be an allowed type erasure of the predecessor
209  Kokkos::Impl::is_compatible_type_erasure<
210  OtherPredecessor, graph_predecessor>::value,
211  int> = 0>
212  /* implicit */
213  GraphNodeRef(
214  GraphNodeRef<execution_space, OtherKernel, OtherPredecessor> const& other)
215  : m_graph_impl(other.m_graph_impl), m_node_impl(other.m_node_impl) {}
216 
217  // Note: because this is an implicit conversion (as is supposed to be the
218  // case with most type-erasing wrappers like this), we don't also need
219  // a converting assignment operator.
220 
221  // </editor-fold> end Type-erasing converting ctor and assignment }}}3
222  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
223 
224  // </editor-fold> end Constructors, destructors, and assignment }}}2
225  //----------------------------------------------------------------------------
226 
227  //----------------------------------------------------------------------------
228  // <editor-fold desc="then_parallel_for"> {{{2
229 
230  // TODO We should do better than a p-for (that uses registers, heavier).
231  // This should "just" launch the function on device with our driver.
232  template <typename Label, typename Functor,
233  typename = std::enable_if_t<std::is_invocable_r_v<
234  void, const Kokkos::Impl::remove_cvref_t<Functor>>>>
235  auto then(Label&& label, const ExecutionSpace& exec,
236  Functor&& functor) const {
237  using next_kernel_t =
238  Kokkos::Impl::GraphNodeThenImpl<ExecutionSpace,
239  Kokkos::Impl::remove_cvref_t<Functor>>;
240  return this->_then_kernel(next_kernel_t{std::forward<Label>(label), exec,
241  std::forward<Functor>(functor)});
242  }
243 
244  template <typename Label, typename Functor,
245  typename = std::enable_if_t<std::is_invocable_r_v<
246  void, const Kokkos::Impl::remove_cvref_t<Functor>>>>
247  auto then(Label&& label, Functor&& functor) const {
248  return this->then(std::forward<Label>(label), ExecutionSpace{},
249  std::forward<Functor>(functor));
250  }
251 
252  template <typename Label, typename Functor>
253  auto then_host(Label&&, Functor&& functor) const {
254  using host_t = Kokkos::Impl::GraphNodeThenHostImpl<
255  ExecutionSpace, Kokkos::Impl::remove_cvref_t<Functor>>;
256  using return_t = GraphNodeRef<ExecutionSpace, host_t, GraphNodeRef>;
257 
258  auto graph_ptr = m_graph_impl.lock();
259  KOKKOS_EXPECTS(bool(graph_ptr))
260 
261  auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
262  m_graph_impl,
263  Kokkos::Impl::GraphAccess::make_node_shared_ptr<
264  typename return_t::node_impl_t>(
265  m_node_impl->execution_space_instance(),
266  Kokkos::Impl::_graph_node_host_ctor_tag{},
267  std::forward<Functor>(functor),
268  Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
269 
270  // Add the node itself to the backend's graph data structure, now that
271  // everything is set up.
272  graph_ptr->add_node(rv.m_node_impl);
273  // Add the predecessor we stored in the constructor above in the
274  // backend's data structure, now that everything is set up.
275  graph_ptr->add_predecessor(rv.m_node_impl, *this);
276  KOKKOS_ENSURES(bool(rv.m_node_impl))
277  return rv;
278  }
279 
280 #if defined(KOKKOS_ENABLE_CUDA) || \
281  (defined(KOKKOS_ENABLE_HIP) && defined(KOKKOS_IMPL_HIP_NATIVE_GRAPH)) || \
282  (defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_IMPL_SYCL_GRAPH_SUPPORT))
283  template <class Functor,
284  typename = std::enable_if_t<std::is_invocable_r_v<
285  void, const Kokkos::Impl::remove_cvref_t<Functor>,
286  const ExecutionSpace&>>>
287 #if defined(KOKKOS_ENABLE_CUDA)
288  auto cuda_capture(const ExecutionSpace& exec, Functor&& functor) const {
289  if constexpr (std::is_same_v<ExecutionSpace, Kokkos::Cuda>) {
290 #elif defined(KOKKOS_ENABLE_HIP) && defined(KOKKOS_IMPL_HIP_NATIVE_GRAPH)
291  auto hip_capture(const ExecutionSpace& exec, Functor&& functor) const {
292  if constexpr (std::is_same_v<ExecutionSpace, Kokkos::HIP>) {
293 #elif defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_IMPL_SYCL_GRAPH_SUPPORT)
294  auto sycl_capture(const ExecutionSpace& exec, Functor&& functor) const {
295  if constexpr (std::is_same_v<ExecutionSpace, Kokkos::SYCL>) {
296 #endif
297  using capture_t = Kokkos::Impl::GraphNodeCaptureImpl<
298  ExecutionSpace, Kokkos::Impl::remove_cvref_t<Functor>>;
299  using return_t = GraphNodeRef<ExecutionSpace, capture_t, GraphNodeRef>;
300 
301  auto graph_ptr = m_graph_impl.lock();
302  KOKKOS_EXPECTS(bool(graph_ptr))
303 
304  auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
305  m_graph_impl,
306  Kokkos::Impl::GraphAccess::make_node_shared_ptr<
307  typename return_t::node_impl_t>(
308  m_node_impl->execution_space_instance(),
309  Kokkos::Impl::_graph_node_capture_ctor_tag{},
310  std::forward<Functor>(functor),
311  Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
312 
313  // Add the node itself to the backend's graph data structure, now that
314  // everything is set up.
315  graph_ptr->add_node(exec, rv.m_node_impl);
316  // Add the predecessor we stored in the constructor above in the
317  // backend's data structure, now that everything is set up.
318  graph_ptr->add_predecessor(rv.m_node_impl, *this);
319  KOKKOS_ENSURES(bool(rv.m_node_impl))
320  return rv;
321  }
322  }
323 #endif
324 
325  template <
326  class Policy, class Functor,
327  std::enable_if_t<
328  // equivalent to:
329  // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
330  is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
331  // --------------------
332  int> = 0>
333  auto then_parallel_for(std::string arg_name, Policy&& arg_policy,
334  Functor&& functor) const {
335  //----------------------------------------
336  KOKKOS_EXPECTS(!m_graph_impl.expired())
337  KOKKOS_EXPECTS(bool(m_node_impl))
338  // TODO @graph restore this expectation once we add comparability to space
339  // instances
340  // KOKKOS_EXPECTS(
341  // arg_policy.space() == m_graph_impl->get_execution_space());
342 
343  // needs to static assert constraint: DataParallelFunctor<Functor>
344 
345  using policy_t = Kokkos::Impl::remove_cvref_t<Policy>;
346  // constraint check: same execution space type (or defaulted, maybe?)
347  static_assert(
348  std::is_same_v<typename policy_t::execution_space, execution_space>,
349  // TODO @graph make defaulted execution space work
350  //|| policy_t::execution_space_is_defaulted,
351  "Execution Space mismatch between execution policy and graph");
352 
353  auto policy = Experimental::require((Policy&&)arg_policy,
354  Kokkos::Impl::KernelInGraphProperty{});
355 
356  using next_policy_t = decltype(policy);
357  using next_kernel_t =
358  Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
359  std::decay_t<Functor>,
360  Kokkos::ParallelForTag>;
361  return this->_then_kernel(next_kernel_t{std::move(arg_name), policy.space(),
362  (Functor&&)functor,
363  (Policy&&)policy});
364  }
365 
366  template <
367  class Policy, class Functor,
368  std::enable_if_t<
369  // equivalent to:
370  // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
371  is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
372  // --------------------
373  int> = 0>
374  auto then_parallel_for(Policy&& policy, Functor&& functor) const {
375  // needs to static assert constraint: DataParallelFunctor<Functor>
376  return this->then_parallel_for("", (Policy&&)policy, (Functor&&)functor);
377  }
378 
379  template <class Functor>
380  auto then_parallel_for(std::string name, std::size_t n,
381  Functor&& functor) const {
382  // needs to static assert constraint: DataParallelFunctor<Functor>
383  return this->then_parallel_for(std::move(name),
385  (Functor&&)functor);
386  }
387 
388  template <class Functor>
389  auto then_parallel_for(std::size_t n, Functor&& functor) const {
390  // needs to static assert constraint: DataParallelFunctor<Functor>
391  return this->then_parallel_for("", n, (Functor&&)functor);
392  }
393 
394  // </editor-fold> end then_parallel_for }}}2
395  //----------------------------------------------------------------------------
396 
397  //----------------------------------------------------------------------------
398  // <editor-fold desc="then_parallel_reduce"> {{{2
399 
400  // Equivalent to std::get<I>(std::tuple) but callable on the device.
401  template <bool B, class T1, class T2>
402  static KOKKOS_FUNCTION std::conditional_t<B, T1&&, T2&&>
403  impl_forwarding_switch(T1&& v1, T2&& v2) {
404  if constexpr (B)
405  return static_cast<T1&&>(v1);
406  else
407  return static_cast<T2&&>(v2);
408  }
409 
410  template <
411  class Policy, class Functor, class ReturnType,
412  std::enable_if_t<
413  // equivalent to:
414  // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
415  is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
416  // --------------------
417  int> = 0>
418  auto then_parallel_reduce(std::string arg_name, Policy&& arg_policy,
419  Functor&& functor,
420  ReturnType&& return_value) const {
421  auto graph_impl_ptr = m_graph_impl.lock();
422  KOKKOS_EXPECTS(bool(graph_impl_ptr))
423  KOKKOS_EXPECTS(bool(m_node_impl))
424  // TODO @graph restore this expectation once we add comparability to space
425  // instances
426  // KOKKOS_EXPECTS(
427  // arg_policy.space() == m_graph_impl->get_execution_space());
428 
429  // needs static assertion of constraint:
430  // DataParallelReductionFunctor<Functor, ReturnType>
431 
432  using policy_t = std::remove_cv_t<std::remove_reference_t<Policy>>;
433  static_assert(
434  std::is_same_v<typename policy_t::execution_space, execution_space>,
435  // TODO @graph make defaulted execution space work
436  // || policy_t::execution_space_is_defaulted,
437  "Execution Space mismatch between execution policy and graph");
438 
439  // This is also just an expectation, but it's one that we expect the user
440  // to interact with (even in release mode), so we should throw an exception
441  // with an explanation rather than just doing a contract assertion.
442  // We can't static_assert this because of the way that Reducers store
443  // whether or not they point to a View as a runtime boolean rather than part
444  // of the type.
445  if (Kokkos::Impl::parallel_reduce_needs_fence(
446  graph_impl_ptr->get_execution_space(), return_value)) {
447  Kokkos::Impl::throw_runtime_exception(
448  "Parallel reductions in graphs can't operate on Reducers that "
449  "reference a scalar because they can't complete synchronously. Use a "
450  "Kokkos::View instead and keep in mind the result will only be "
451  "available once the graph is submitted (or in tasks that depend on "
452  "this one).");
453  }
454 
455  //----------------------------------------
456  // This is a disaster, but I guess it's not a my disaster to fix right now
457  using return_type_remove_cvref =
458  std::remove_cv_t<std::remove_reference_t<ReturnType>>;
459  static_assert(Kokkos::is_view<return_type_remove_cvref>::value ||
460  Kokkos::is_reducer<return_type_remove_cvref>::value,
461  "Output argument to parallel reduce in a graph must be a "
462  "View or a Reducer");
463 
464  if constexpr (Kokkos::is_reducer_v<return_type_remove_cvref>) {
465  static_assert(
467  ExecutionSpace, typename return_type_remove_cvref::
468  result_view_type::memory_space>::accessible,
469  "The reduction target must be accessible by the graph execution "
470  "space.");
471  } else {
472  static_assert(
474  ExecutionSpace,
475  typename return_type_remove_cvref::memory_space>::accessible,
476  "The reduction target must be accessible by the graph execution "
477  "space.");
478  }
479 
480  using return_type =
481  // Yes, you do really have to do this...
482  std::conditional_t<Kokkos::is_reducer<return_type_remove_cvref>::value,
483  return_type_remove_cvref,
484  const return_type_remove_cvref>;
485  using functor_type = Kokkos::Impl::remove_cvref_t<Functor>;
486  // see Kokkos_Parallel_Reduce.hpp for how these details are used there;
487  // we're just doing the same thing here
488  using return_value_adapter =
489  Kokkos::Impl::ParallelReduceReturnValue<void, return_type,
490  functor_type>;
491  // End of Kokkos reducer disaster
492  //----------------------------------------
493 
494  auto policy = Experimental::require((Policy&&)arg_policy,
495  Kokkos::Impl::KernelInGraphProperty{});
496 
497  using passed_reducer_type = typename return_value_adapter::reducer_type;
498 
499  constexpr bool passed_reducer_type_is_invalid =
500  std::is_same_v<InvalidType, passed_reducer_type>;
501  using TheReducerType =
502  std::conditional_t<passed_reducer_type_is_invalid, functor_type,
503  passed_reducer_type>;
504 
505  using analysis = Kokkos::Impl::FunctorAnalysis<
506  Kokkos::Impl::FunctorPatternInterface::REDUCE, Policy, TheReducerType,
507  typename return_value_adapter::value_type>;
508  typename analysis::Reducer final_reducer(
509  impl_forwarding_switch<passed_reducer_type_is_invalid>(functor,
510  return_value));
511  Kokkos::Impl::CombinedFunctorReducer<functor_type,
512  typename analysis::Reducer>
513  functor_reducer(functor, final_reducer);
514 
515  using next_policy_t = decltype(policy);
516  using next_kernel_t =
517  Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
518  decltype(functor_reducer),
519  Kokkos::ParallelReduceTag>;
520 
521  return this->_then_kernel(next_kernel_t{
522  std::move(arg_name), graph_impl_ptr->get_execution_space(),
523  functor_reducer, (Policy&&)policy,
524  return_value_adapter::return_value(return_value, functor)});
525  }
526 
527  template <
528  class Policy, class Functor, class ReturnType,
529  std::enable_if_t<
530  // equivalent to:
531  // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
532  is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
533  // --------------------
534  int> = 0>
535  auto then_parallel_reduce(Policy&& arg_policy, Functor&& functor,
536  ReturnType&& return_value) const {
537  return this->then_parallel_reduce("", (Policy&&)arg_policy,
538  (Functor&&)functor,
539  (ReturnType&&)return_value);
540  }
541 
542  template <class Functor, class ReturnType>
543  auto then_parallel_reduce(std::string label,
544  typename execution_space::size_type idx_end,
545  Functor&& functor,
546  ReturnType&& return_value) const {
547  return this->then_parallel_reduce(
548  std::move(label), Kokkos::RangePolicy<execution_space>{0, idx_end},
549  (Functor&&)functor, (ReturnType&&)return_value);
550  }
551 
552  template <class Functor, class ReturnType>
553  auto then_parallel_reduce(typename execution_space::size_type idx_end,
554  Functor&& functor,
555  ReturnType&& return_value) const {
556  return this->then_parallel_reduce("", idx_end, (Functor&&)functor,
557  (ReturnType&&)return_value);
558  }
559 
560  // </editor-fold> end then_parallel_reduce }}}2
561  //----------------------------------------------------------------------------
562 
563  // TODO @graph parallel scan, deep copy, etc.
564 };
565 
566 } // end namespace Experimental
567 } // end namespace Kokkos
568 
569 #endif // KOKKOS_KOKKOS_GRAPHNODE_HPP
Can AccessSpace access MemorySpace ?
ReturnType
Execution policy for work over a range of an integral type.