PLSSVM - Parallel Least Squares Support Vector Machine  2.0.0
A Least Squares Support Vector Machine implementation using different backends.
parameter.hpp
Go to the documentation of this file.
1 
12 #ifndef PLSSVM_PARAMETER_HPP_
13 #define PLSSVM_PARAMETER_HPP_
14 #pragma once
15 
16 #include "plssvm/default_value.hpp" // plssvm::default_value, plssvm::is_default_value_v
17 #include "plssvm/detail/type_traits.hpp" // PLSSVM_REQUIRES, plssvm::detail::{remove_cvref_t, always_false_v}
18 #include "plssvm/detail/utility.hpp" // plssvm::detail::unreachable
19 #include "plssvm/kernel_function_types.hpp" // plssvm::kernel_function_type, plssvm::kernel_function_type_to_math_string
20 
21 #include "fmt/core.h" // fmt::format
22 #include "fmt/ostream.h" // be able to output custom types with an operator<< overload using fmt
23 #include "igor/igor.hpp" // IGOR_MAKE_NAMED_ARGUMENT, igor::parser, igor::has_unnamed_arguments, igor::has_other_than
24 
25 #include <iostream> // std::clog, std::endl, std::ostream
26 #include <string_view> // std::string_view
27 #include <type_traits> // std::is_same_v, std::is_convertible_v
28 #include <utility> // std::forward
29 
30 namespace plssvm {
31 
33 // create named arguments
35 IGOR_MAKE_NAMED_ARGUMENT(kernel_type);
37 IGOR_MAKE_NAMED_ARGUMENT(gamma);
39 IGOR_MAKE_NAMED_ARGUMENT(degree);
41 IGOR_MAKE_NAMED_ARGUMENT(coef0);
43 IGOR_MAKE_NAMED_ARGUMENT(cost);
45 IGOR_MAKE_NAMED_ARGUMENT(epsilon);
47 IGOR_MAKE_NAMED_ARGUMENT(max_iter);
49 IGOR_MAKE_NAMED_ARGUMENT(sycl_implementation_type);
51 IGOR_MAKE_NAMED_ARGUMENT(sycl_kernel_invocation_type);
53 
54 namespace detail {
55 
59 template <typename... Args>
60 constexpr bool has_only_named_args_v = !igor::has_unnamed_arguments<Args...>();
61 
65 template <typename... Args>
66 constexpr bool has_only_parameter_named_args_v = !igor::has_other_than<Args...>(plssvm::kernel_type, plssvm::gamma, plssvm::degree, plssvm::coef0, plssvm::cost);
67 
71 template <typename... Args>
72 constexpr bool has_only_sycl_parameter_named_args_v = !igor::has_other_than<Args...>(plssvm::kernel_type, plssvm::gamma, plssvm::degree, plssvm::coef0, plssvm::cost, plssvm::sycl_implementation_type, plssvm::sycl_kernel_invocation_type);
73 
83 template <typename ExpectedType, typename IgorParser, typename NamedArgType>
84 ExpectedType get_value_from_named_parameter(const IgorParser &parser, const NamedArgType &named_arg) {
85  using parsed_named_arg_type = detail::remove_cvref_t<decltype(parser(named_arg))>;
86  // check whether a plssvm::default_value (e.g., plssvm::default_value<double>) or unwrapped normal value (e.g., double) has been provided
87  if constexpr (is_default_value_v<parsed_named_arg_type>) {
88  static_assert(std::is_convertible_v<typename parsed_named_arg_type::value_type, ExpectedType>, "Cannot convert the wrapped default value to the expected type!");
89  // a plssvm::default_value has been provided (e.g., plssvm::default_value<double>)
90  return static_cast<ExpectedType>(parser(named_arg).value());
91  } else if constexpr (std::is_convertible_v<parsed_named_arg_type, ExpectedType>) {
92  // an unwrapped value has been provided (e.g., double)
93  return static_cast<ExpectedType>(parser(named_arg));
94  } else {
95  static_assert(detail::always_false_v<ExpectedType>, "The named parameter must be of type plssvm::default_value or a fundamental type!");
96  }
97  // may never be reached
99 }
100 
105 template <typename T>
106 struct parameter {
107  // only float and doubles are allowed
108  static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "The template type can only be 'float' or 'double'!");
109 
111  using real_type = T;
112 
116  constexpr parameter() noexcept = default;
125  constexpr parameter(const kernel_function_type kernel_p, const int degree_p, const real_type gamma_p, const real_type coef0_p, const real_type cost_p) noexcept {
126  // not in member initializer list since we want to override the default value
127  kernel_type = kernel_p;
128  degree = degree_p;
129  gamma = gamma_p;
130  coef0 = coef0_p;
131  cost = cost_p;
132  }
133 
140  template <typename... Args, PLSSVM_REQUIRES(has_only_named_args_v<Args...>)>
141  constexpr explicit parameter(const parameter &params, Args &&...named_args) :
142  parameter{ params } {
143  this->set_named_arguments(std::forward<Args>(named_args)...);
144  }
145 
151  template <typename... Args, PLSSVM_REQUIRES(has_only_named_args_v<Args...>)>
152  constexpr explicit parameter(Args &&...named_args) noexcept {
153  this->set_named_arguments(std::forward<Args>(named_args)...);
154  }
155 
166 
172  template <typename U>
173  [[nodiscard]] constexpr explicit operator parameter<U>() const {
174  if constexpr (std::is_same_v<U, real_type>) {
175  // no special conversions needed
176  return parameter<real_type>{ *this };
177  } else {
178  // convert between parameter<float> <-> parameter<double>
180  }
181  }
182 
191  [[nodiscard]] constexpr bool equivalent(const parameter &other) const noexcept {
192  // equality check, but only the member variables that a necessary for the current kernel type are compared!
193  // cannot be equal if both parameters have different kernel types
194  if (kernel_type != other.kernel_type) {
195  return false;
196  }
197  // check member variables based on kernel type
198  switch (kernel_type.value()) {
200  return cost == other.cost;
202  return degree == other.degree && gamma == other.gamma && coef0 == other.coef0 && cost == other.cost;
204  return gamma == other.gamma && cost == other.cost;
205  }
206  return false;
207  }
208 
209  private:
215  template <typename... Args>
216  void set_named_arguments(Args &&...named_args) {
217  igor::parser parser{ std::forward<Args>(named_args)... };
218 
219  // compile time check: only named parameter are permitted
220  static_assert(!parser.has_unnamed_arguments(), "Can only use named parameter!");
221  // compile time check: each named parameter must only be passed once
222  static_assert(!parser.has_duplicates(), "Can only use each named parameter once!");
223  // compile time check: only some named parameters are allowed
224  static_assert(!parser.has_other_than(plssvm::kernel_type, plssvm::gamma, plssvm::degree, plssvm::coef0, plssvm::cost, plssvm::sycl_implementation_type, plssvm::sycl_kernel_invocation_type),
225  "An illegal named parameter has been passed!");
226 
227  // shorthand function for emitting a warning if a provided parameter is not used by the current kernel function
228  [[maybe_unused]] const auto print_warning = [](const std::string_view param_name, const kernel_function_type kernel) {
229  std::clog << fmt::format("{} parameter provided, which is not used in the {} kernel ({})!", param_name, kernel, kernel_function_type_to_math_string(kernel)) << std::endl;
230  };
231 
232  // compile time/runtime check: the values must have the correct types
233  if constexpr (parser.has(plssvm::kernel_type)) {
234  // get the value of the provided named parameter
235  kernel_type = get_value_from_named_parameter<typename decltype(kernel_type)::value_type>(parser, plssvm::kernel_type);
236  }
237  if constexpr (parser.has(plssvm::gamma)) {
238  // get the value of the provided named parameter
239  gamma = get_value_from_named_parameter<typename decltype(gamma)::value_type>(parser, plssvm::gamma);
240  // runtime check: the value may only be used with a specific kernel type
242  print_warning("gamma", kernel_type);
243  }
244  }
245  if constexpr (parser.has(plssvm::degree)) {
246  // get the value of the provided named parameter
247  degree = get_value_from_named_parameter<typename decltype(degree)::value_type>(parser, plssvm::degree);
248  // runtime check: the value may only be used with a specific kernel type
250  print_warning("degree", kernel_type);
251  }
252  }
253  if constexpr (parser.has(plssvm::coef0)) {
254  // get the value of the provided named parameter
255  coef0 = get_value_from_named_parameter<typename decltype(coef0)::value_type>(parser, plssvm::coef0);
256  // runtime check: the value may only be used with a specific kernel type
258  print_warning("coef0", kernel_type);
259  }
260  }
261  if constexpr (parser.has(plssvm::cost)) {
262  // get the value of the provided named parameter
263  cost = get_value_from_named_parameter<typename decltype(cost)::value_type>(parser, plssvm::cost);
264  }
265  }
266 };
267 
268 extern template struct parameter<float>;
269 extern template struct parameter<double>;
270 
279 template <typename T>
280 [[nodiscard]] constexpr bool operator==(const parameter<T> &lhs, const parameter<T> &rhs) noexcept {
281  return lhs.kernel_type == rhs.kernel_type && lhs.degree == rhs.degree && lhs.gamma == rhs.gamma && lhs.coef0 == rhs.coef0 && lhs.cost == rhs.cost;
282 }
291 template <typename T>
292 [[nodiscard]] constexpr bool operator!=(const parameter<T> &lhs, const parameter<T> &rhs) noexcept {
293  return !(lhs == rhs);
294 }
295 
305 template <typename T>
306 [[nodiscard]] constexpr bool equivalent(const parameter<T> &lhs, const parameter<T> &rhs) noexcept {
307  return lhs.equivalent(rhs);
308 }
309 
317 template <typename T>
318 std::ostream &operator<<(std::ostream &out, const parameter<T> &params);
319 
320 } // namespace detail
321 
329 
333 [[nodiscard]] constexpr bool equivalent(const parameter &lhs, const parameter &rhs) noexcept {
334  return detail::equivalent(lhs, rhs);
335 }
336 
337 } // namespace plssvm
338 
339 #endif // PLSSVM_PARAMETER_HPP_
constexpr const value_type & value() const noexcept
Get the currently active value: the user provided value if provided, otherwise the default value is r...
Definition: default_value.hpp:150
Implements a class used to be able to distinguish between the default value of a variable and an assi...
Defines universal utility functions.
Defines an enumeration holding all possible kernel function types.
std::remove_cv_t< std::remove_reference_t< T > > remove_cvref_t
Remove the topmost reference- and cv-qualifiers.
Definition: type_traits.hpp:46
constexpr bool has_only_named_args_v
Trait to check whether Args only contains named-parameter.
Definition: parameter.hpp:60
constexpr bool operator!=(const parameter< T > &lhs, const parameter< T > &rhs) noexcept
Compares the two parameter sets lhs and rhs for inequality.
Definition: parameter.hpp:292
ExpectedType get_value_from_named_parameter(const IgorParser &parser, const NamedArgType &named_arg)
Parse the value hold be named_arg and return it converted to the ExpectedType.
Definition: parameter.hpp:84
void unreachable()
Invokes undefined behavior. Used to mark code paths that may never be reachable.
Definition: utility.hpp:32
constexpr bool equivalent(const parameter< T > &lhs, const parameter< T > &rhs) noexcept
Checks whether the two parameter sets lhs and rhs are equivalent.
Definition: parameter.hpp:306
constexpr bool has_only_parameter_named_args_v
Trait to check whether Args only contains named-parameter that can be used to initialize a plssvm::pa...
Definition: parameter.hpp:66
std::ostream & operator<<(std::ostream &out, const execution_range &range)
Output the execution range to the given output-stream out.
constexpr bool operator==(const parameter< T > &lhs, const parameter< T > &rhs) noexcept
Compares the two parameter sets lhs and rhs for equality.
Definition: parameter.hpp:280
constexpr bool has_only_sycl_parameter_named_args_v
Trait to check whether Args only contains named-parameter that can be used to initialize a plssvm::pa...
Definition: parameter.hpp:72
The main namespace containing all public API functions.
Definition: backend_types.hpp:24
kernel_function_type
Enum class for all implemented kernel functions.
Definition: kernel_function_types.hpp:31
constexpr bool equivalent(const parameter &lhs, const parameter &rhs) noexcept
Checks whether the two parameter sets lhs and rhs are equivalent.
Definition: parameter.hpp:333
std::string_view kernel_function_type_to_math_string(kernel_function_type kernel) noexcept
Return the mathematical representation of the kernel_type kernel.
This class denotes an explicit default value initialization used to distinguish between the default v...
Definition: default_value.hpp:37
Class for encapsulating all important C-SVM parameters.
Definition: parameter.hpp:106
default_value< real_type > cost
The cost parameter in the C-SVM.
Definition: parameter.hpp:165
constexpr parameter(const parameter &params, Args &&...named_args)
Construct a parameter by using the values in params and overwriting all values using the provided nam...
Definition: parameter.hpp:141
default_value< real_type > coef0
The coef0 parameter used in the polynomial kernel function.
Definition: parameter.hpp:163
constexpr parameter(Args &&...named_args) noexcept
Construct a parameter set by overwriting the SVM parameters' default values that are provided using n...
Definition: parameter.hpp:152
default_value< int > degree
The degree parameter used in the polynomial kernel function.
Definition: parameter.hpp:159
void set_named_arguments(Args &&...named_args)
Overwrite the default values of this parameter object with the potential provided named-parameters na...
Definition: parameter.hpp:216
T real_type
The type of the data. Must be either float or double.
Definition: parameter.hpp:111
constexpr bool equivalent(const parameter &other) const noexcept
Checks whether the current parameter set is equivalent to the one given by other.
Definition: parameter.hpp:191
default_value< kernel_function_type > kernel_type
The used kernel function: linear, polynomial, or radial basis functions (rbf).
Definition: parameter.hpp:157
constexpr parameter() noexcept=default
Default construct a parameter set, i.e., each SVM parameter has its default value.
default_value< real_type > gamma
The gamma parameter used in the polynomial and rbf kernel functions.
Definition: parameter.hpp:161
Defines some generic type traits used in the PLSSVM library.
#define PLSSVM_REQUIRES(...)
A shorthand macro for the std::enable_if_t type trait.
Definition: type_traits.hpp:33