diff --git a/FuncVarPattern.h b/FuncVarPattern.h new file mode 100644 index 0000000000000000000000000000000000000000..d5df46e0f0fb653b826a4567fbcc426f159411d3 --- /dev/null +++ b/FuncVarPattern.h @@ -0,0 +1,227 @@ +#ifndef fvp_FuncVarPattern_HH +#define fvp_FuncVarPattern_HH +#include <array> +#include <cmath> +#include <functional> +#include <iostream> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +// See the excellent Fluentcpp blog series on strong types. +// https://www.fluentcpp.com/2017/05/23/strong-types-inheriting-functionalities-from-underlying/ +#include "NamedType/named_type.hpp" + +// function variable pattern = fvp +namespace fvp { + + using namespace fluent; + + //@{ + /** Helpers. + */ + template <typename T> + struct is_tuple_impl : std::false_type {}; + template <typename... Ts> + struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {}; + template <typename T> + struct is_tuple : is_tuple_impl<std::decay_t<T>> {}; + //@} + + /** Named strong type helper. + * + */ + template <typename Tag> + using Var = NamedType<double, Tag, Addable, Multiplicable, Printable, + ImplicitlyConvertibleTo<double>::templ>; + + /** Forward declaration of mixin. + * + */ + template <typename> + struct ConstructedFunctionSet; + + /** FunctionSet: new variables defined by functions. + * + * \param FVars should be a FVariables template class. + * \param Fs is a tuple of lambdas that calculate values of NewVars_t + * types. + * + */ + template <typename FVars, typename Fs, + typename = typename std::enable_if<is_tuple<Fs>::value>::type> + struct FunctionSet : FVars { + using IndependentVariables_t = typename FVars::IndependentVariables_t; + using InputVars_t = typename FVars::InputVars_t; + using NewVars_t = typename FVars::NewVars_t; + using Vars_t = typename FVars::Vars_t; + using VariableFuncs_t = Fs; + + static const size_t N_input_vars = FVars::N_input_vars; + static const size_t N_new_vars = FVars::N_new_vars; + static const size_t N_vars = FVars::N_vars; + + VariableFuncs_t functions; + + FunctionSet(const FVars& u, const Fs& f) : FVars(u), functions(f) {} + FunctionSet(const FunctionSet& fs) : FVars(fs), functions(fs.functions) {} + + // void Print() const {} + + constexpr Vars_t ComputeValues(const IndependentVariables_t& v) const { + if constexpr (std::is_same<IndependentVariables_t, InputVars_t>::value) { + return std::tuple_cat(v, ComputeNewVars(v)); + } else { + auto vars = FVars::input_variable_set.ComputeValues(v); + return std::tuple_cat(vars, ComputeNewVars(vars)); + } + } + + template <std::size_t I, typename F, typename Args> + static constexpr auto eval_vars2(const F& f, const Args& a) { + using Return_t = typename std::tuple_element<I, NewVars_t>::type; + return Return_t{f(a)}; + } + template <typename Funcs, typename Args, std::size_t... I> + static constexpr auto eval_vars(const Funcs& f, const Args& a, std::index_sequence<I...>) { + return std::make_tuple(eval_vars2<I>(std::get<I>(f), a)...); + } + constexpr auto ComputeNewVars(const InputVars_t& v) const { + return eval_vars(functions, v, std::make_index_sequence<N_new_vars>{}); + } + constexpr auto operator()(const InputVars_t& v) const { return ComputeNewVars(v); } + }; + + /** Definable Function Variables. + * + * Used to construct a FunctionSet from lambdas and wraps the lambda + * arguments with a set of standard variables. + * + * \param FVars Function variables. + * \param Mixin Mixin which adds the ability to declare/define more variables + * to FVariables.. + * + */ + template <typename FVars, template <typename> class Mixin> + struct Definable : FVars { + using InputVars_t = typename FVars::InputVars_t; + using NewVars_t = typename FVars::NewVars_t; + // using FVars::Print; + + template <typename... Ts> + Definable(Ts&&... t) : FVars(std::forward<Ts>(t)...) {} + + template <typename... Funcs> + constexpr auto operator()(const Funcs&... fs) const noexcept { + return t2t_impl(*this, std::make_tuple(fs...), std::make_index_sequence<sizeof...(fs)>{}); + } + + template <typename TUP, std::size_t... I> + static constexpr auto t2t_impl(const Definable<FVars, Mixin>& fd, const TUP& t, + std::index_sequence<I...>) noexcept { + constexpr auto l_wrap = [](const auto& f) constexpr { + return [&](const InputVars_t& args) { return f(args); }; + }; + using type = decltype(std::make_tuple(l_wrap(std::get<I>(t))...)); + return Mixin<FunctionSet<FVars, type>>(FVars{fd}, std::make_tuple(l_wrap(std::get<I>(t))...)); + } + }; + + /** Function Variables (to be defined with a lambda). + * + * \param VarSet is the previously defined variables + * \param NewVars... is a pack of new types computed from the InputVars. + * + * Note the functions relating the new to the old have not yet been + * specified.. + * + */ + template <typename VarSet, typename... NewVars> + struct FVariables { + using VarSet_t = VarSet; + using IndependentVariables_t = typename VarSet::IndependentVariables_t; + using InputVars_t = typename VarSet::Vars_t; + using NewVars_t = std::tuple<NewVars...>; + using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>())); + static const size_t N_input_vars = std::tuple_size<InputVars_t>::value; + static const size_t N_new_vars = std::tuple_size<NewVars_t>::value; + static const size_t N_vars = std::tuple_size<Vars_t>::value; + + VarSet_t input_variable_set; + + FVariables() noexcept {} + FVariables(const VarSet& f) noexcept : input_variable_set(f) {} + FVariables(const FVariables& t) noexcept : input_variable_set(t.input_variable_set) {} + + // void Print() const { + // // std::cout << __PRETTY_FUNCTION__ << "\n"; + //} + }; + + /** Function variable specialization where the variable is an independent variable nad thus has no + * lambda to calculate it. The pack Vs should be left empty. + */ + template <typename... InVars, typename... Vs> + struct FVariables<std::tuple<InVars...>, Vs...> { + using IndependentVariables_t = std::tuple<InVars...>; + using InputVars_t = IndependentVariables_t; + using NewVars_t = std::tuple<Vs...>; + using Vars_t = decltype(std::tuple_cat(std::declval<InputVars_t>(), std::declval<NewVars_t>())); + static const size_t N_input_vars = std::tuple_size<InputVars_t>::value; + static const size_t N_new_vars = std::tuple_size<NewVars_t>::value; + static const size_t N_vars = std::tuple_size<Vars_t>::value; + + constexpr FVariables() noexcept {} + + // void Print() const { + // // std::cout << __PRETTY_FUNCTION__ << "\n"; + //} + }; + + /** Mixin class for FunctionSet that adds the ability to add more variables. + * \param M is a FunctionSet< FV<FS,NewVars>, lambdas... > + */ + template <typename M> + struct ConstructedFunctionSet : M { + + ConstructedFunctionSet(const ConstructedFunctionSet& fs) : M(fs) {} + + template <typename... Ts> + constexpr ConstructedFunctionSet(Ts&&... t) noexcept : M(std::forward<Ts>(t)...) {} + + template <typename... Vs> + constexpr auto make_vars() const noexcept { + return Definable<FVariables<M, Vs...>, ConstructedFunctionSet>(M(*this)); + } + template <typename... Vs, typename... Fs> + constexpr auto add(const Fs&... fs) const noexcept { + return make_vars<Vs...>()(fs...); + } + }; + + template <typename T, typename... Vs> + auto make_vars() { + return Definable<FVariables<T, Vs...>, ConstructedFunctionSet>{}; + } + + /** Helper making independent vars using FVariables specialization. + */ + template <typename... Vs> + auto make_independent_vars() { + return Definable<FVariables<std::tuple<Vs...>>, ConstructedFunctionSet>{}(); + } + + template <class...> + struct is_FunctionSet : public std::false_type {}; + + template <class... Vs> + struct is_FunctionSet<ConstructedFunctionSet<Vs...>> : public std::true_type {}; + + template <class... Vs> + struct is_FunctionSet<FunctionSet<Vs...>> : public std::true_type {}; + +} // namespace fvp + +#endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2098abdb2775b1186a4983c9b102eb6294e20c70 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +all : func_pattern kinematics + +func_pattern : func_pattern.cxx + g++ -o test -std=c++17 func_pattern.cxx -I. + +test_asm_O3 : func_pattern.cxx + g++ -S -o test3.asm -std=c++17 func_pattern.cxx -I. -O3 + +test_asm_O0 : func_pattern.cxx + g++ -S -o test0.asm -std=c++17 func_pattern.cxx -I. -O0 + +kinematics : FuncVarPattern.h kinematics.cxx + g++ -o kinematics -std=c++17 kinematics.cxx -I. -O3 + +kinematics_O0 : FuncVarPattern.h kinematics.cxx + g++ -S -o kinematics0.asm -std=c++17 kinematics.cxx -I. -O0 + +kinematics_O3 : FuncVarPattern.h kinematics.cxx + g++ -S -o kinematics3.asm -std=c++17 kinematics.cxx -I. -O3 + + +clean: + rm -f kinematics + rm -f test + rm -f test0.asm + rm -f test3.asm + rm -f kinematics0.asm + rm -f kinematics3.asm + + diff --git a/kinematics.cxx b/kinematics.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ae6d7efd621a4ab520d0d08945d13dca1780d47c --- /dev/null +++ b/kinematics.cxx @@ -0,0 +1,77 @@ +#include "FuncVarPattern.h" + + +int main() { + using namespace fvp; + + // Note in the future meta classes might help with the type/tag declarations + using E_beam_v = Var<struct E_beam_tag>; + using E_prime_v = Var<struct E_prime_tag>; + using theta_v = Var<struct theta_tag>; + + using Q2_v = Var<struct Q2_tag>; + using nu_v = Var<struct nu_tag>; + using x_v = Var<struct x_tag>; + using y_v = Var<struct y_tag>; + using W_v = Var<struct W_tag>; + + double M_p = 0.938; + + auto measured_vars = make_independent_vars<E_beam_v, E_prime_v, theta_v>(); + + auto dis_kine_vars = measured_vars + .add<Q2_v, nu_v>( + [](const auto& v) constexpr { + const auto& E0 = std::get<E_beam_v>(v); + const auto& Ep = std::get<E_prime_v>(v); + const auto& th = std::get<theta_v>(v); + const double M_p = 0.938; + const double sinth = std::sin(th / 2.0); + return 4.0 * E0 * Ep * sinth * sinth; + }, + [](const auto& v) constexpr { + const auto& E0 = std::get<E_beam_v>(v); + const auto& Ep = std::get<E_prime_v>(v); + return E0 - Ep; + }) // note that we are chaining here. + .add<x_v, y_v>( + [](const auto& v) constexpr { + // calculate x + const auto& Q2 = std::get<Q2_v>(v); + const auto& nu = std::get<nu_v>(v); + //std::cout << Q2 << "\n"; + //std::cout << nu << "\n"; + double M_p = 0.938; + return Q2/(2.0*M_p*nu); + }, + [](const auto& v) constexpr { + //calculate y + const auto& E0 = std::get<E_beam_v>(v); + const auto& nu = std::get<nu_v>(v); + return nu/E0; + }) + .add<W_v>( + [](const auto& v) constexpr { + // calculate W + const double M_p = 0.938; + const auto& Q2 = std::get<Q2_v>(v); + const auto& nu = std::get<nu_v>(v); + return std::sqrt(M_p*M_p - Q2 + 2.0*M_p*nu); + }); + + auto input = std::make_tuple(E_beam_v{10.6}, E_prime_v{4.7}, theta_v{17.0*M_PI/180.0}); + + auto dis_values = dis_kine_vars.ComputeValues(input); + + // could probably make this better, like: dis_values.get<x_v>() + std::cout << " E0 = " << std::get<E_beam_v>(dis_values) << "\n"; + std::cout << " E' = " << std::get<E_prime_v>(dis_values) << "\n"; + std::cout << " th = " << std::get<theta_v>(dis_values)*180.0/M_PI << "\n"; + std::cout << " Q2 = " << std::get<Q2_v>(dis_values) << "\n"; + std::cout << " x = " << std::get<x_v>(dis_values) << "\n"; + std::cout << " W = " << std::get<W_v>(dis_values) << "\n"; + std::cout << " y = " << std::get<y_v>(dis_values) << "\n"; + std::cout << " nu = " << std::get<nu_v>(dis_values) << "\n"; + +} +