Algorithm.cuh 15.1 KB
Newer Older
1
2
3
/*****************************************************************************\
* (c) Copyright 2018-2020 CERN for the benefit of the LHCb Collaboration      *
\*****************************************************************************/
4
5
#pragma once

Daniel Campora's avatar
Daniel Campora committed
6
#include "BackendCommon.h"
7
8
#include "Logger.h"
#include "BaseTypes.cuh"
9
#include "TargetFunction.cuh"
10
#include "Argument.cuh"
11
#include "Contract.h"
12
13
14
15
16
#include "RuntimeOptions.h"
#include "Constants.cuh"
#include "HostBuffers.cuh"
#include <any>

Daniel Campora's avatar
Daniel Campora committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace {
  // Get the ArgumentRefManagerType from the function operator()
  template<typename Function>
  struct FunctionTraits;

  template<typename Function, typename... Ts, typename... OtherArguments>
  struct FunctionTraits<void (Function::*)(const ArgumentRefManager<Ts...>&, OtherArguments...) const> {
    using ArgumentRefManagerType = ArgumentRefManager<Ts...>;
  };

  template<typename Algorithm>
  struct AlgorithmTraits {
    using ArgumentRefManagerType = typename FunctionTraits<decltype(&Algorithm::operator())>::ArgumentRefManagerType;
  };
31
32
33
34
35
36
37
38
39

  // Creates a std::array store out of the vector one
  template<std::size_t... Is>
  auto create_store_ref(
    const std::vector<std::reference_wrapper<ArgumentData>>& vector_store_ref,
    std::index_sequence<Is...>)
  {
    return std::array {vector_store_ref[Is]...};
  }
Daniel Campora's avatar
Daniel Campora committed
40
41
} // namespace

42
namespace Allen {
Daniel Campora's avatar
Daniel Campora committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  template<typename ContractsTuple, typename Enabled = void>
  struct AlgorithmContracts;

  template<>
  struct AlgorithmContracts<std::tuple<>, void> {
    using preconditions = std::tuple<>;
    using postconditions = std::tuple<>;
  };

  template<typename A, typename... T>
  struct AlgorithmContracts<
    std::tuple<A, T...>,
    std::enable_if_t<std::is_base_of_v<Allen::contract::Precondition, A>>> {
    using recursive_contracts = AlgorithmContracts<std::tuple<T...>>;
    using preconditions = append_to_tuple_t<typename recursive_contracts::preconditions, A>;
    using postconditions = typename recursive_contracts::postconditions;
  };

  template<typename A, typename... T>
  struct AlgorithmContracts<
    std::tuple<A, T...>,
    std::enable_if_t<std::is_base_of_v<Allen::contract::Postcondition, A>>> {
    using recursive_contracts = AlgorithmContracts<std::tuple<T...>>;
    using preconditions = typename recursive_contracts::preconditions;
    using postconditions = append_to_tuple_t<typename recursive_contracts::postconditions, A>;
  };

Daniel Campora's avatar
Daniel Campora committed
70
71
72
73
74
75
#if __GNUC__ == 11
// Deal with spurious -Wnonnull from GCC 11 dbg builds
// Perhaps https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96003
#pragma GCC diagnostic ignored "-Wnonnull"
#endif

76
  // Type-erased algorithm
Gerhard Raven's avatar
Gerhard Raven committed
77
78
  class TypeErasedAlgorithm {
    struct vtable {
79
80
      std::string (*name)(void const*) = nullptr;
      std::any (*create_arg_ref_manager)(
Gerhard Raven's avatar
Gerhard Raven committed
81
82
        std::vector<std::reference_wrapper<ArgumentData>>,
        std::vector<std::vector<std::reference_wrapper<ArgumentData>>>) = nullptr;
83
84
85
86
87
88
89
90
91
92
      void (*set_arguments_size)(void*, std::any&, const RuntimeOptions&, const Constants&, const HostBuffers&) =
        nullptr;
      void (
        *invoke)(void const*, std::any&, const RuntimeOptions&, const Constants&, HostBuffers&, const Allen::Context&) =
        nullptr;
      void (*init)(void*) = nullptr;
      void (*set_properties)(void*, const std::map<std::string, std::string>&) = nullptr;
      std::map<std::string, std::string> (*get_properties)(void const*) = nullptr;
      std::string (*scope)() = nullptr;
      void (*dtor)(void*) = nullptr;
Daniel Campora's avatar
Daniel Campora committed
93
94
95
96
97
98
99
100
101
102
103
104
      void (*run_preconditions)(
        void* p,
        std::any& arg_ref_manager,
        const RuntimeOptions& runtime_options,
        const Constants& constants,
        const Allen::Context& context) = nullptr;
      void (*run_postconditions)(
        void* p,
        std::any& arg_ref_manager,
        const RuntimeOptions& runtime_options,
        const Constants& constants,
        const Allen::Context& context) = nullptr;
Gerhard Raven's avatar
Gerhard Raven committed
105
106
107
    };

    void* instance = nullptr;
Daniel Campora's avatar
Daniel Campora committed
108
    vtable table = {};
109
110
111

  public:
    template<typename ALGORITHM>
Daniel Campora's avatar
Daniel Campora committed
112
    TypeErasedAlgorithm(std::in_place_type_t<ALGORITHM>, const std::string& name)
113
114
115
116
    {
      auto p = new ALGORITHM {};
      p->set_name(name);
      instance = p;
Daniel Campora's avatar
Daniel Campora committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
      table = vtable {
        [](void const* p) { return static_cast<ALGORITHM const*>(p)->name(); },
        [](
          std::vector<std::reference_wrapper<ArgumentData>> vector_store_ref,
          std::vector<std::vector<std::reference_wrapper<ArgumentData>>> input_aggregates) {
          using arg_ref_mgr_t = typename AlgorithmTraits<ALGORITHM>::ArgumentRefManagerType;
          using store_ref_t = typename arg_ref_mgr_t::store_ref_t;
          using input_aggregates_t = typename arg_ref_mgr_t::input_aggregates_t;
          if (std::tuple_size_v<store_ref_t> != vector_store_ref.size()) {
            throw std::runtime_error("unexpected number of arguments");
          }
          auto store_ref =
            create_store_ref(vector_store_ref, std::make_index_sequence<std::tuple_size_v<store_ref_t>> {});
          auto input_agg_store = input_aggregates_t {Allen::gen_input_aggregates_tuple(
            input_aggregates, std::make_index_sequence<std::tuple_size_v<input_aggregates_t>> {})};
          return std::any {arg_ref_mgr_t {store_ref, input_agg_store}};
        },
        [](
          void* p,
          std::any& arg_ref_manager,
          const RuntimeOptions& runtime_options,
          const Constants& constants,
          const HostBuffers& host_buffers) {
          using arg_ref_mgr_t = typename AlgorithmTraits<ALGORITHM>::ArgumentRefManagerType;
          static_cast<ALGORITHM*>(p)->set_arguments_size(
            std::any_cast<arg_ref_mgr_t&>(arg_ref_manager), runtime_options, constants, host_buffers);
        },
        [](
          const void* p,
          std::any& arg_ref_manager,
          const RuntimeOptions& runtime_options,
          const Constants& constants,
          HostBuffers& host_buffers,
          const Allen::Context& context) {
          using arg_ref_mgr_t = typename AlgorithmTraits<ALGORITHM>::ArgumentRefManagerType;
          static_cast<ALGORITHM const*>(p)->operator()(
            std::any_cast<arg_ref_mgr_t&>(arg_ref_manager), runtime_options, constants, host_buffers, context);
        },
        [](void* p) {
          if constexpr (Allen::has_init_member_fn<ALGORITHM>::value) {
            initialize_algorithm(*static_cast<ALGORITHM*>(p));
          }
          else {
            _unused(p);
          }
        },
        [](void* p, const std::map<std::string, std::string>& algo_config) {
          static_cast<ALGORITHM*>(p)->set_properties(algo_config);
        },
        [](void const* p) { return static_cast<ALGORITHM const*>(p)->get_properties(); },
        []() -> std::string { return ALGORITHM::algorithm_scope; },
        [](void* p) { delete static_cast<ALGORITHM*>(p); },
        [](
          void* p,
          std::any& arg_ref_manager,
          const RuntimeOptions& runtime_options,
          const Constants& constants,
          const Allen::Context& context) {
          using arg_ref_mgr_t = typename AlgorithmTraits<ALGORITHM>::ArgumentRefManagerType;
          using preconditions_t = typename AlgorithmContracts<typename ALGORITHM::contracts>::preconditions;
Gitlab CI's avatar
Gitlab CI committed
177
          if constexpr (std::tuple_size_v<preconditions_t>> 0) {
Daniel Campora's avatar
Daniel Campora committed
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
            auto preconditions = preconditions_t {};
            const auto location = static_cast<ALGORITHM const*>(p)->name();
            std::apply(
              [&](auto&... contract) { (contract.set_location(location, demangle<decltype(contract)>()), ...); },
              preconditions);
            std::apply(
              [&](const auto&... contract) {
                (std::invoke(
                   contract, std::any_cast<arg_ref_mgr_t&>(arg_ref_manager), runtime_options, constants, context),
                 ...);
              },
              preconditions);
          }
        },
        [](
          void* p,
          std::any& arg_ref_manager,
          const RuntimeOptions& runtime_options,
          const Constants& constants,
          const Allen::Context& context) {
          using arg_ref_mgr_t = typename AlgorithmTraits<ALGORITHM>::ArgumentRefManagerType;
          using postconditions_t = typename AlgorithmContracts<typename ALGORITHM::contracts>::postconditions;
Gitlab CI's avatar
Gitlab CI committed
200
          if constexpr (std::tuple_size_v<postconditions_t>> 0) {
Daniel Campora's avatar
Daniel Campora committed
201
202
203
204
205
206
207
208
209
210
211
212
213
214
            auto postconditions = postconditions_t {};
            const auto location = static_cast<ALGORITHM const*>(p)->name();
            std::apply(
              [&](auto&... contract) { (contract.set_location(location, demangle<decltype(contract)>()), ...); },
              postconditions);
            std::apply(
              [&](const auto&... contract) {
                (std::invoke(
                   contract, std::any_cast<arg_ref_mgr_t&>(arg_ref_manager), runtime_options, constants, context),
                 ...);
              },
              postconditions);
          }
        }};
Gerhard Raven's avatar
Gerhard Raven committed
215
    }
Daniel Campora's avatar
Daniel Campora committed
216
    ~TypeErasedAlgorithm() { (table.dtor)(instance); }
Gerhard Raven's avatar
Gerhard Raven committed
217
    TypeErasedAlgorithm(const TypeErasedAlgorithm&) = delete;
218
219
    TypeErasedAlgorithm(TypeErasedAlgorithm&& arg) : instance {std::exchange(arg.instance, nullptr)}, table {arg.table}
    {}
Gerhard Raven's avatar
Gerhard Raven committed
220
221
222
    TypeErasedAlgorithm& operator=(const TypeErasedAlgorithm&) = delete;
    TypeErasedAlgorithm& operator=(TypeErasedAlgorithm&&) = delete;

Gitlab CI's avatar
Gitlab CI committed
223
    std::string name() const { return (table.name)(instance); }
224
225
226
227
    std::any create_arg_ref_manager(
      std::vector<std::reference_wrapper<ArgumentData>> vector_store_ref,
      std::vector<std::vector<std::reference_wrapper<ArgumentData>>> input_aggregates)
    {
Gitlab CI's avatar
Gitlab CI committed
228
      return (table.create_arg_ref_manager)(std::move(vector_store_ref), std::move(input_aggregates));
Gerhard Raven's avatar
Gerhard Raven committed
229
    }
230
231
232
233
234
235
    void set_arguments_size(
      std::any& arg_ref_manager,
      const RuntimeOptions& runtime_options,
      const Constants& constants,
      const HostBuffers& host_buffers)
    {
Daniel Campora's avatar
Daniel Campora committed
236
      (table.set_arguments_size)(instance, arg_ref_manager, runtime_options, constants, host_buffers);
Gerhard Raven's avatar
Gerhard Raven committed
237
    }
238
239
240
241
242
243
244
    void invoke(
      std::any& arg_ref_manager,
      const RuntimeOptions& runtime_options,
      const Constants& constants,
      HostBuffers& host_buffers,
      const Allen::Context& context)
    {
Daniel Campora's avatar
Daniel Campora committed
245
      (table.invoke)(instance, arg_ref_manager, runtime_options, constants, host_buffers, context);
Gerhard Raven's avatar
Gerhard Raven committed
246
    }
Daniel Campora's avatar
Daniel Campora committed
247
    void init() { (table.init)(instance); }
248
249
    void set_properties(const std::map<std::string, std::string>& algo_config)
    {
Daniel Campora's avatar
Daniel Campora committed
250
      (table.set_properties)(instance, algo_config);
Gerhard Raven's avatar
Gerhard Raven committed
251
    }
Gitlab CI's avatar
Gitlab CI committed
252
253
    std::map<std::string, std::string> get_properties() const { return (table.get_properties)(instance); }
    std::string scope() const { return (table.scope)(); }
Daniel Campora's avatar
Daniel Campora committed
254
255
256
257
258
259
    void run_preconditions(
      std::any& arg_ref_manager,
      const RuntimeOptions& runtime_options,
      const Constants& constants,
      const Allen::Context& context)
    {
Gitlab CI's avatar
Gitlab CI committed
260
      return (table.run_preconditions)(instance, arg_ref_manager, runtime_options, constants, context);
Daniel Campora's avatar
Daniel Campora committed
261
262
263
264
265
266
267
    }
    void run_postconditions(
      std::any& arg_ref_manager,
      const RuntimeOptions& runtime_options,
      const Constants& constants,
      const Allen::Context& context)
    {
Gitlab CI's avatar
Gitlab CI committed
268
      return (table.run_postconditions)(instance, arg_ref_manager, runtime_options, constants, context);
Daniel Campora's avatar
Daniel Campora committed
269
    }
270
271
  };

Daniel Campora's avatar
Daniel Campora committed
272
273
274
275
#if __GNUC__ == 11
#pragma GCC diagnostic pop
#endif

276
277
  // Tool to instantiate algorithms
  template<typename T>
Gerhard Raven's avatar
Gerhard Raven committed
278
279
  TypeErasedAlgorithm instantiate_algorithm(const std::string& name);

280
281
282
283
284
285
#define INSTANTIATE_ALGORITHM(TYPE)                                                      \
  template<>                                                                             \
  Allen::TypeErasedAlgorithm Allen::instantiate_algorithm<TYPE>(const std::string& name) \
  {                                                                                      \
    return TypeErasedAlgorithm {std::in_place_type<TYPE>, name};                         \
  }
286

287
288
289
290
291
292
293
294
295
296
297
  // Forward declare to use in Algorithm
  template<typename V>
  class Property;

  /**
   * @brief      In addition to functionality in BaseAlgorithm, algorithms may need to access properties shared with
   * other algorithms
   *
   */
  class Algorithm : public BaseAlgorithm {
  public:
Daniel Campora's avatar
Daniel Campora committed
298
299
300
    // Define empty contract container by default
    using contracts = std::tuple<>;

301
302
303
    template<typename T>
    using Property = Allen::Property<T>;

Daniel Campora's avatar
Daniel Campora committed
304
    Algorithm() = default;
Daniel Campora's avatar
Daniel Campora committed
305
    Algorithm(const Algorithm&) = delete;
Daniel Campora's avatar
Daniel Campora committed
306
307
308
309
    Algorithm& operator=(const Algorithm&) = delete;
    Algorithm(Algorithm&&) = delete;
    Algorithm& operator=(Algorithm&&) = delete;

310
311
312
313
314
315
    void set_properties(const std::map<std::string, std::string>& algo_config) override
    {
      for (auto kv : algo_config) {
        auto it = m_properties.find(kv.first);

        if (it == m_properties.end()) {
316
          std::cerr << "could not set " << kv.first << "=" << kv.second << "\n";
317
          const std::string error_message = "property " + kv.first + " does not exist";
318
          throw std::runtime_error {error_message};
319
320
        }
        else {
321
          it->second->from_string(kv.second);
322
323
324
325
        }
      }
    }

326
327
328
329
330
331
332
    template<typename T, typename R>
    void set_property_value(const R& value)
    {
      auto prop = const_cast<Allen::Property<T>*>(dynamic_cast<Allen::Property<T> const*>(get_prop(T::name)));
      prop->set_value(value);
    }

333
334
335
336
    // Gets the value of property with type T
    template<typename T>
    T property() const
    {
337
338
      const auto base_prop = get_prop(T::name);
      const auto prop = dynamic_cast<const Property<T>*>(base_prop);
339
      if (!prop) {
Gitlab CI's avatar
Gitlab CI committed
340
341
        const std::string error_message =
          "property " + std::string(T::name) + " not defined, perhaps member definition is missing";
342
343
344
        throw std::runtime_error {error_message};
      }
      return prop->get_value();
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    }

    std::map<std::string, std::string> get_properties() const override
    {
      std::map<std::string, std::string> properties;
      for (const auto& kv : m_properties) {
        properties.emplace(kv.first, kv.second->to_string());
      }
      return properties;
    }

    bool register_property(std::string const& name, BaseProperty* property) override
    {
      auto r = m_properties.emplace(name, property);
      if (!std::get<1>(r)) {
360
361
        const std::string error_message = "could not register property " + name;
        throw std::runtime_error {error_message};
362
363
364
365
      }
      return std::get<1>(r);
    }

366
    // Setter and getter of name of the algorithm
367
    void set_name(const std::string& name) { m_name = name; }
368

369
    std::string name() const { return m_name; }
370

371
372
    template<typename Fn>
    auto host_function(const Fn& fn) const
373
    {
374
      return HostFunction<Fn> {m_properties, fn};
375
376
    }

377
    template<typename Fn>
378
    auto global_function(const Fn& fn) const
379
    {
380
      return GlobalFunction<Fn> {m_properties, fn};
381
    }
Gitlab CI's avatar
Gitlab CI committed
382

383
384
    PROPERTY(verbosity_t, "verbosity", "verbosity of algorithm", int);

385
  protected:
386
387
388
389
390
    BaseProperty const* get_prop(const std::string& prop_name) const override
    {
      if (m_properties.find(prop_name) != m_properties.end()) {
        return m_properties.at(prop_name);
      }
391
      return nullptr;
392
    }
393

394
395
  private:
    std::map<std::string, BaseProperty*> m_properties;
396
    std::string m_name = "";
397
    Property<verbosity_t> m_verbosity = {this, 3};
398
  };
399
} // namespace Allen