Skip to content

Commit

Permalink
Merge pull request #334 from bluescarni/pr/step_cb
Browse files Browse the repository at this point in the history
Improved step callback API
  • Loading branch information
bluescarni authored Aug 6, 2023
2 parents ab6650f + edeec7d commit 3846d17
Show file tree
Hide file tree
Showing 15 changed files with 1,205 additions and 158 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ set(HEYOKA_SRC_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/src/detail/string_conv.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/detail/math_wrappers.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/detail/logging_impl.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/step_callback.cpp"
# NOTE: sleef.cpp needs to be compiled even if we are not
# building with sleef support on.
"${CMAKE_CURRENT_SOURCE_DIR}/src/detail/sleef.cpp"
Expand Down
8 changes: 8 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Changelog
New
~~~

- The step callbacks can now optionally implement a ``pre_hook()``
member function that will be called before the first step
is taken by a ``propagate_*()`` function
(`#334 <https://github.com/bluescarni/heyoka/pull/334>`__).
- The heyoka library now passes all ``clang-tidy`` checks
(`#315 <https://github.com/bluescarni/heyoka/pull/315>`__).
- Introduce several vectorised overloads in the expression
Expand Down Expand Up @@ -39,6 +43,10 @@ New
Changes
~~~~~~~

- The step callbacks are now copied in :ref:`ensemble propagations <tut_ensemble>`
rather then being shared among threads. The aim of this change
is to reduce the likelihood of data races
(`#334 <https://github.com/bluescarni/heyoka/pull/334>`__).
- Comprehensive overhaul of the expression system, including:
enhanced automatic simplification capabilities for sums,
products and powers, removal of several specialised primitives
Expand Down
32 changes: 30 additions & 2 deletions doc/tut_adaptive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ integration will be ``taylor_outcome::err_nf_state``.
.. versionadded:: 0.7.0

The ``propagate_for()`` and ``propagate_until()`` functions
can be invoked with two additional optional keyword arguments:
can be invoked with additional optional keyword arguments:

- ``max_delta_t``: similarly to the ``step()`` function, this value
represents the maximum timestep size in absolute value;
Expand All @@ -249,6 +249,21 @@ can be invoked with two additional optional keyword arguments:
will continue after the invocation of the callback, otherwise the integration
will be interrupted.

.. versionadded:: 1.0.0

Optionally, a function object callback can implement a ``pre_hook()`` member
function with signature

.. code-block:: c++

void pre_hook(taylor_adaptive<double> &);


that will be invoked once *before* the first step is taken
by the ``propagate_for()`` and ``propagate_until()`` functions.
- ``c_output``: a boolean flag that enables :ref:`continuous output <tut_c_output>`.


Propagation over a time grid
----------------------------

Expand Down Expand Up @@ -297,7 +312,7 @@ fact that they must be finite and ordered monotonically).
.. versionadded:: 0.7.0

The ``propagate_grid()`` function
can be invoked with two additional optional keyword arguments:
can be invoked with additional optional keyword arguments:

- ``max_delta_t``: similarly to the ``step()`` function, this value
represents the maximum timestep size in absolute value;
Expand All @@ -312,6 +327,19 @@ can be invoked with two additional optional keyword arguments:
will continue after the invocation of the callback, otherwise the integration
will be interrupted.

.. versionadded:: 1.0.0

Optionally, a function object callback can implement a ``pre_hook()`` member
function with signature

.. code-block:: c++

void pre_hook(taylor_adaptive<double> &);


that will be invoked once *before* the first step is taken
by the ``propagate_grid()`` function.

Full code listing
-----------------

Expand Down
25 changes: 10 additions & 15 deletions doc/tut_ensemble.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The signature of the generator ``gen`` reads:

That is, the generator takes in input a *copy* of the template integrator ``ta``
and an iteration index ``idx`` in the ``[0, n_iter)`` range. ``gen`` is then expected
to modify the copy of ``ta`` (e.g., by setting its initial conditions to specific
to modify ``ta`` (e.g., by setting its initial conditions to specific
values) and return it.

The ``ensemble_propagate_until()`` function iterates over
Expand Down Expand Up @@ -151,22 +151,17 @@ In an ensemble propagation, it is thus important to keep in mind that
the following actions may be performed
concurrently by separate threads of execution:

* invocation of the generator's call operator and of the call operator
of the callback that can (optionally) be passed to the ``propagate_*()``
functions. In other words, both the generator and the ``propagate_*()``
callback are shared among several threads of execution and used
concurrently;
* copy construction of the events callbacks and invocation of the
call operator on the copies. That is, each thread of execution
* invocation of the generator's call operator (that is, the generator is
shared among multiple threads and must support concurrent invocations
of its call operator);
* copy construction of the callback that can (optionally) be passed to
the ``propagate_*()`` functions (that is, step callbacks are **not**
shared among multiple threads of execution, and a new copy is created for each
invocation of the ``propagate_*()`` functions);
* copy construction of the events callbacks (that is, each thread of execution
gets its own copy of the event callbacks thanks to the creation
of a new integrator object via the generator.
of a new integrator object via the generator).

For instance, an event callback which performs write operations
on a global variable without using some form of synchronisation
will result in undefined behaviour when used in an ensemble propagation.

Another example of unsafe usage is a ``propagate_*()`` callback that
performs write operations into its own data member(s) without
synchronisation: because the
``propagate_*()`` callback is shared among several threads, such usage results
in a data race.
20 changes: 6 additions & 14 deletions include/heyoka/callable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ class HEYOKA_DLL_PUBLIC_INLINE_CLASS callable<R(Args...)>

// NOTE: generic ctor is enabled only if it does not
// compete with copy/move ctors.
// NOTE: unlike std::function, if f is a nullptr or an empty std::function/callable
// the constructed callable will *NOT* be empty. If we need this,
// we can implement it with some meta-programming.
template <typename T, std::enable_if_t<std::negation_v<std::is_same<callable, detail::uncvref_t<T>>>, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
callable(T &&f) : callable(std::forward<T>(f), std::is_same<R(Args...), detail::uncvref_t<T>>{})
{
}
Expand Down Expand Up @@ -252,9 +256,7 @@ struct tracking_level<heyoka::detail::callable_inner<T, R, Args...>> {
// NOTE: these are verbatim re-implementations of the BOOST_CLASS_EXPORT_KEY
// and BOOST_CLASS_EXPORT_IMPLEMENT macros, which do not work well with class templates.
#define HEYOKA_S11N_CALLABLE_EXPORT_KEY(...) \
namespace boost \
{ \
namespace serialization \
namespace boost::serialization \
{ \
template <> \
struct guid_defined<heyoka::detail::callable_inner<__VA_ARGS__>> : boost::mpl::true_ { \
Expand All @@ -265,17 +267,10 @@ struct tracking_level<heyoka::detail::callable_inner<T, R, Args...>> {
/* NOTE: the stringize here will produce a name enclosed by brackets. */ \
return BOOST_PP_STRINGIZE((heyoka::detail::callable_inner<__VA_ARGS__>)); \
} \
} \
}

#define HEYOKA_S11N_CALLABLE_EXPORT_IMPLEMENT(...) \
namespace boost \
{ \
namespace archive \
{ \
namespace detail \
{ \
namespace extra_detail \
namespace boost::archive::detail::extra_detail \
{ \
template <> \
struct init_guid<heyoka::detail::callable_inner<__VA_ARGS__>> { \
Expand All @@ -286,9 +281,6 @@ struct tracking_level<heyoka::detail::callable_inner<T, R, Args...>> {
= ::boost::serialization::singleton< \
guid_initializer<heyoka::detail::callable_inner<__VA_ARGS__>>>::get_mutable_instance() \
.export_guid(); \
} \
} \
} \
}

#define HEYOKA_S11N_CALLABLE_EXPORT(...) \
Expand Down
13 changes: 7 additions & 6 deletions include/heyoka/ensemble_propagate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <heyoka/detail/type_traits.hpp>
#include <heyoka/detail/visibility.hpp>
#include <heyoka/step_callback.hpp>
#include <heyoka/taylor.hpp>

HEYOKA_BEGIN_NAMESPACE
Expand All @@ -38,20 +39,20 @@ HEYOKA_DLL_PUBLIC
std::vector<std::tuple<taylor_adaptive<T>, taylor_outcome, T, T, std::size_t, std::optional<continuous_output<T>>>>
ensemble_propagate_until_impl(const taylor_adaptive<T> &, T, std::size_t,
const std::function<taylor_adaptive<T>(taylor_adaptive<T>, std::size_t)> &,
std::size_t, T, const std::function<bool(taylor_adaptive<T> &)> &, bool, bool);
std::size_t, T, step_callback<T> &, bool, bool);

template <typename T>
HEYOKA_DLL_PUBLIC
std::vector<std::tuple<taylor_adaptive<T>, taylor_outcome, T, T, std::size_t, std::optional<continuous_output<T>>>>
ensemble_propagate_for_impl(const taylor_adaptive<T> &, T, std::size_t,
const std::function<taylor_adaptive<T>(taylor_adaptive<T>, std::size_t)> &, std::size_t,
T, const std::function<bool(taylor_adaptive<T> &)> &, bool, bool);
T, step_callback<T> &, bool, bool);

template <typename T>
HEYOKA_DLL_PUBLIC std::vector<std::tuple<taylor_adaptive<T>, taylor_outcome, T, T, std::size_t, std::vector<T>>>
ensemble_propagate_grid_impl(const taylor_adaptive<T> &, std::vector<T>, std::size_t,
const std::function<taylor_adaptive<T>(taylor_adaptive<T>, std::size_t)> &, std::size_t, T,
const std::function<bool(taylor_adaptive<T> &)> &);
step_callback<T> &);

} // namespace detail

Expand Down Expand Up @@ -104,20 +105,20 @@ HEYOKA_DLL_PUBLIC std::vector<std::tuple<taylor_adaptive_batch<T>, std::optional
ensemble_propagate_until_batch_impl(
const taylor_adaptive_batch<T> &, T, std::size_t,
const std::function<taylor_adaptive_batch<T>(taylor_adaptive_batch<T>, std::size_t)> &, std::size_t,
const std::vector<T> &, const std::function<bool(taylor_adaptive_batch<T> &)> &, bool, bool);
const std::vector<T> &, step_callback_batch<T> &, bool, bool);

template <typename T>
HEYOKA_DLL_PUBLIC std::vector<std::tuple<taylor_adaptive_batch<T>, std::optional<continuous_output_batch<T>>>>
ensemble_propagate_for_batch_impl(
const taylor_adaptive_batch<T> &, T, std::size_t,
const std::function<taylor_adaptive_batch<T>(taylor_adaptive_batch<T>, std::size_t)> &, std::size_t,
const std::vector<T> &, const std::function<bool(taylor_adaptive_batch<T> &)> &, bool, bool);
const std::vector<T> &, step_callback_batch<T> &, bool, bool);

template <typename T>
HEYOKA_DLL_PUBLIC std::vector<std::tuple<taylor_adaptive_batch<T>, std::vector<T>>> ensemble_propagate_grid_batch_impl(
const taylor_adaptive_batch<T> &, const std::vector<T> &, std::size_t,
const std::function<taylor_adaptive_batch<T>(taylor_adaptive_batch<T>, std::size_t)> &, std::size_t,
const std::vector<T> &, const std::function<bool(taylor_adaptive_batch<T> &)> &);
const std::vector<T> &, step_callback_batch<T> &);

} // namespace detail

Expand Down
Loading

0 comments on commit 3846d17

Please sign in to comment.