Skip to content
Snippets Groups Projects
Commit 329ec9ff authored by Whitney Armstrong's avatar Whitney Armstrong
Browse files

Added header library version and new kinematics example.

- FuncVarPattern.h : Name sucks but it can always be changed.
- kinematics.cxx   : Calculates DIS kinematics as an example

Ideas:
- Add a wrapper on the result tuple so that `std::get<some_var>(res)` -> `res.get<some_var>()`.
parent 745732c8
Branches master
No related tags found
No related merge requests found
#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
Makefile 0 → 100644
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
#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";
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment