diff --git a/include/expolit/differentiation.hh b/include/expolit/differentiation.hh index c00e2a0..8faed4a 100644 --- a/include/expolit/differentiation.hh +++ b/include/expolit/differentiation.hh @@ -1,123 +1,143 @@ /** * @file * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2018 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Expolit is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Expolit is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with Expolit. If not, see . * */ #ifndef DIFFERENTIATION_HH #define DIFFERENTIATION_HH #include "exponential.hh" #include "litteral.hh" #include "operations.hh" #include "polynomial.hh" #include "types.hh" #include namespace expolit { +namespace detail { +template +struct for_diff { + template + constexpr static void diff(std::array& res, + const std::array& a) { + std::get(res) = a[i + 1] * static_cast(i + 1); + for_diff::diff(res, a); + } +}; + +template <> +struct for_diff<0> { + template + constexpr static void diff(std::array& res, + const std::array& a) { + std::get<0>(res) = a[1] * static_cast(1); + } +}; +} // namespace detail + /// Differentiate polynomial template constexpr auto differentiate(const Polynomial& p) { Polynomial dp; - for (UInt i = 0; i < dp.terms; ++i) - dp.coeffs[i] = p.coeffs[i + 1] * static_cast(i + 1); + detail::for_diff::diff(dp.coeffs, p.coeffs); return dp; } /// Differentiate constant template constexpr auto differentiate(const Constant& /*p*/) { return Constant({0}); } /// Differentiate litteral template constexpr auto differentiate(const Litteral& /*p*/) { return 0; } /// Differentiate exponential template constexpr auto differentiate(const Exponential& e) { return differentiate(e.expression) * e; } /// Differentiate generic product template constexpr auto differentiate(const Product& p) { auto&& o = p.operands; return differentiate(o.first) * o.second + o.first * differentiate(o.second); } /// Differentiate constant + expression template constexpr auto differentiate(const Sum, U>& p) { return differentiate(p.operands.second); } /// Differentiate expression + constant template constexpr auto differentiate(const Sum>& p) { return differentiate(p.commute()); } /// Differentiate litteral + expression template constexpr auto differentiate(const Sum, U>& p) { return differentiate(p.operands.second); } /// Differentiate expression + litteral template constexpr auto differentiate(const Sum>& p) { return differentiate(p.commute()); } /// Differentiate constant * expression template constexpr auto differentiate(const Product, U>& p) { return p.operands.first * differentiate(p.operands.second); } /// Differentiate expression * constant template constexpr auto differentiate(const Product>& p) { return differentiate(p.commute()); } /// Differentiate litteral * expression template constexpr auto differentiate(const Product, U>& p) { return p.operands.first * differentiate(p.operands.second); } /// Differentiate expression * litteral template constexpr auto differentiate(const Product>& p) { return differentiate(p.commute()); } } // namespace expolit #endif diff --git a/include/expolit/integration.hh b/include/expolit/integration.hh index e818965..b002233 100644 --- a/include/expolit/integration.hh +++ b/include/expolit/integration.hh @@ -1,134 +1,155 @@ /** * @file * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2018 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Expolit is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Expolit is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with Expolit. If not, see . * */ #ifndef INTEGRATION_HH #define INTEGRATION_HH #include "differentiation.hh" #include "exponential.hh" #include "litteral.hh" #include "polynomial.hh" #include "types.hh" namespace expolit { + +namespace detail { +template +struct for_integ { + template + static constexpr void integ(std::array& res, + const std::array& a) { + std::get(res) = a[i-1] / static_cast(i); + for_integ::integ(res, a); + } +}; + +template <> +struct for_integ<1> { + template + static constexpr void integ(std::array& res, + const std::array& a) { + std::get<1>(res) = a[0]; + } +}; +} // namespace detail + /// Integrate polynomial template constexpr auto integrate(const Polynomial& p) { Polynomial ip; - for (UInt i = 1; i < ip.terms; ++i) - ip.coeffs[i] = p.coeffs[i - 1] / static_cast(i); + detail::for_integ::integ(ip.coeffs, p.coeffs); return ip; } /// Integrate litteral template constexpr auto integrate(const Litteral& q) { constexpr Polynomial id({0, 1}); return q * id; } /// Integrate constant * expression template constexpr auto integrate(const Product, Derived>& p) { return p.operands.first * integrate(p.operands.second); } /// Integrate expression * constant template constexpr auto integrate(const Product>& p) { return integrate(p.commute()); } /// Integrate litteral * expression template constexpr auto integrate(const Product, Derived>& p) { return p.operands.first * integrate(p.operands.second); } /// Integrate expression * litteral template constexpr auto integrate(const Product>& p) { return integrate(p.commute()); } /// Integrate litteral / expression template constexpr auto integrate(const Division>& p) { return integrate(p.operands.first) / p.operands.second; } /// Integrate exponential of order 1 polynomial template constexpr auto integrate(const Exponential>& e) { return Constant({1 / e.expression.coeffs.back()}) * e; } /// Integrate exponential of litteral * order 1 polynomial template constexpr auto integrate(const Exponential, Polynomial>>& e) { return e * Constant({1 / e.expression.operands.second.coeffs.back()}) / e.expression.operands.first; } /// Integrate exponential of order 1 polynomial * litteral template constexpr auto integrate(const Exponential, Litteral>>& e) { return integrate(e.op(e.expression.commute())); } /// Integration by parts of expression * polynomial template constexpr auto integrate(const Product>& p) { auto u = integrate(p.operands.first); return u * p.operands.second - integrate(u * differentiate(p.operands.second)); } /// Integration by parts of polynomial * expression template constexpr auto integrate(const Product, Derived>& p) { return integrate(p.commute()); } /// Integration of sum template constexpr auto integrate(const Sum& s) { return integrate(s.operands.first) + integrate(s.operands.second); } /// Compute definite integral template constexpr auto definite_integral(const std::pair& bounds, const Expression& exp) { auto integ = integrate(exp.downcast()); return integ(bounds.second) - integ(bounds.first); } } // namespace expolit #endif diff --git a/include/expolit/litteral.hh b/include/expolit/litteral.hh index 40b40c6..7023274 100644 --- a/include/expolit/litteral.hh +++ b/include/expolit/litteral.hh @@ -1,79 +1,80 @@ /** * @file * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2018 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Expolit is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Expolit is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with Expolit. If not, see . * */ #ifndef LITTERAL_HH #define LITTERAL_HH #include "operations.hh" #include "polynomial.hh" #include "types.hh" namespace expolit { template -struct Litteral : public Expression> { +struct Litteral : Expression> { using tag = Tag; + Litteral() = default; }; /// Substitue litteral template constexpr auto substitute(const Expression& val, const Litteral& /*t*/) { return val.downcast(); } /// Substitute litteral in binary operation template constexpr auto substitute(const Expression& val, const BinaryOperation& op) { return op.op(substitute(val, op.operands.first), substitute(val, op.operands.second)); } /// Substitute litteral in polynomial (aka do nothing) template constexpr auto substitute(const Expression& /*val*/, const Polynomial& p) { return p; } /// Substitute litteral in polynomial (aka do nothing) template constexpr auto substitute(const Expression& val, const UnaryOperation& p) { return p.op(substitute(val, p.expression)); } /// Substitute generic expression by downcast // template // constexpr auto substitute(const Expression& val, // const Expression& e) { // return substitute(val, e.downcast()); // } } // namespace expolit #endif diff --git a/include/expolit/operations.hh b/include/expolit/operations.hh index f3d3546..32407a9 100644 --- a/include/expolit/operations.hh +++ b/include/expolit/operations.hh @@ -1,180 +1,189 @@ /** * @file * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2018 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Expolit is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Expolit is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with Expolit. If not, see . * */ #ifndef OPERATIONS_HH #define OPERATIONS_HH #include "types.hh" #include #include #include namespace expolit { /** * @brief Type for unary operation */ template -struct UnaryOperation : public Expression> { +struct UnaryOperation : Expression> { /// Construct from an expression constexpr UnaryOperation(const Expression& t) : expression(t.downcast()) {} /// Evaluate operation template constexpr auto operator()(Val&& v) const { return op(expression(v)); } const T expression; const Op op = Op(); }; /** * @brief Type for binary operation * * Handles binary operations between two arbitrary expressions */ template -struct BinaryOperation : public Expression> { +struct BinaryOperation : Expression> { /// Construct from two expressions constexpr BinaryOperation(const Expression& t, const Expression& u) : operands(t.downcast(), u.downcast()) {} /// Evaluate operation template constexpr auto operator()(Val&& v) const { return op(operands.first(v), operands.second(v)); } constexpr auto commute() const { return BinaryOperation(operands.second, operands.first); } /// Product operarands const std::pair operands; const Op op = Op(); }; /// Symmetric of T, U template using SBinaryOperation = BinaryOperation; /// Product expression template using Product = BinaryOperation>; /// Sum expression template using Sum = BinaryOperation>; /// Difference expression template using Difference = BinaryOperation>; /// Division expression template using Division = BinaryOperation>; /* -------------------------------------------------------------------------- */ /* Algebraic rules */ /* -------------------------------------------------------------------------- */ /// Product operator template constexpr auto operator*(const Expression& e1, const Expression& e2) { return Product(e1, e2); } /// Sum operator template constexpr auto operator+(const Expression& e1, const Expression& e2) { return Sum(e1, e2); } /// Difference operator template constexpr auto operator-(const Expression& e1, const Expression& e2) { return Difference(e1, e2); } /// Division operator template constexpr auto operator/(const Expression& e1, - const Expression& e2) { + const Expression& e2) { return Division(e1, e2); } namespace detail { -template -constexpr auto power_acc(const Expression& acc, - const Expression& e) { - if constexpr (n > 1) - return power_acc(acc.downcast() * e.downcast(), e); - else +template +struct power_acc { + template + static constexpr auto accumulate(const Expression& acc, + const Expression& e) { + return power_acc::accumulate(acc.downcast() * e.downcast(), e); + } +}; + +template <> +struct power_acc<1> { + template + static constexpr auto accumulate(const Expression& acc, + const Expression&) { return acc.downcast(); -} + } +}; + } // namespace detail template constexpr auto pow(const Expression& e) { - return detail::power_acc(e, e); + return detail::power_acc::accumulate(e, e); } - /* -------------------------------------------------------------------------- */ /* Output */ /* -------------------------------------------------------------------------- */ template std::ostream& operator<<(std::ostream& os, const Product& p) { os << "(" << p.operands.first << ")" << " * " << "(" << p.operands.second << ")"; return os; } template std::ostream& operator<<(std::ostream& os, const Division& p) { os << "(" << p.operands.first << ")" << " / " << "(" << p.operands.second << ")"; return os; } template std::ostream& operator<<(std::ostream& os, const Difference& p) { os << p.operands.first << " - " << p.operands.second; return os; } template std::ostream& operator<<(std::ostream& os, const Sum& p) { os << p.operands.first << " + " << p.operands.second; return os; } } // namespace expolit #endif diff --git a/include/expolit/polynomial.hh b/include/expolit/polynomial.hh index 960e65b..2743f73 100644 --- a/include/expolit/polynomial.hh +++ b/include/expolit/polynomial.hh @@ -1,232 +1,318 @@ /** * @file * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2018 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Expolit is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Expolit is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with Expolit. If not, see . * */ #ifndef POLYNOMIALS_HH #define POLYNOMIALS_HH #include "operations.hh" #include "types.hh" #include #include #include #include namespace expolit { template -using compatible = std::enable_if_t>; +using compatible = std::enable_if_t::value>; /** * @brief Polynomial type * * Type for polynomial expressions of arbitrary order, variable type and * coefficient type */ template -struct Polynomial : public Expression> { +struct Polynomial : Expression> { /// Number of terms constexpr static UInt terms = order + 1; constexpr Polynomial() = default; constexpr explicit Polynomial(const std::array& coeffs) : coeffs(coeffs) {} /// Evaluating polynomial template > constexpr auto operator()(Val z) const { using Res = decltype(std::declval() * z); Res res(coeffs.front()); Val power(z); for (UInt i = 1; i < terms; ++i) { res += coeffs[i] * power; power *= z; } return res; } /// Storing coefficients std::array coeffs = {0}; }; template using Constant = Polynomial; /* -------------------------------------------------------------------------- */ /* Algebraic rules */ /* -------------------------------------------------------------------------- */ /// Sum with scalar template > constexpr auto operator+(DT x, const Polynomial& p) { Polynomial() + x), order> res(p); - res.coeffs.front() += x; + std::get<0>(res.coeffs) += x; return res; } /// Sum with scalar template > constexpr auto operator+(const Polynomial& p, DT x) { return x + p; } +namespace detail { +template +struct for_scalar { + template + constexpr static void prod(std::array& res, DT x) { + std::get(res) *= x; + for_scalar::prod(res, x); + } + + template + constexpr static void div(std::array& res, DT x) { + std::get(res) /= x; + for_scalar::prod(res, x); + } +}; + +template <> +struct for_scalar<0> { + template + constexpr static void prod(std::array& res, DT x) { + std::get<0>(res) *= x; + } + + template + constexpr static void div(std::array& res, DT x) { + std::get<0>(res) /= x; + } +}; +} // namespace detail + /// Product with scalar template > constexpr auto operator*(DT x, const Polynomial& p) { Polynomial() * x), order> res(p); - for (auto&& v : res.coeffs) - v *= x; + detail::for_scalar::prod(res.coeffs, x); return res; } /// Product with scalar template > constexpr auto operator*(const Polynomial& p, DT x) { return x * p; } /// Division with scalar template > constexpr auto operator/(const Polynomial& p, DT x) { Polynomial() / x), order> res(p); - for (auto&& v : res.coeffs) - v /= x; + detail::for_scalar::div(res.coeffs, x); return res; } /// Division with constant template > constexpr auto operator/(const Polynomial& p, const Constant& x) { Polynomial() / x.coeffs.front()), order> res(p); - for (auto&& v : res.coeffs) - v /= x.coeffs.front(); + detail::for_scalar::div(res.coeffs, std::get<0>(x.coeffs)); return res; } +namespace detail { +template +struct coeff_product { + template + static constexpr void prod(std::array& res, + const std::array& a, + const std::array& b) { + std::get(res) += a[i] * b[j]; + coeff_product::prod(res, a, b); + } +}; + +template +struct coeff_product { + template + static constexpr void prod(std::array& res, + const std::array& a, + const std::array& b) { + std::get(res) += a[i] * b[0]; + coeff_product::prod(res, a, b); + } +}; + +template <> +struct coeff_product<0, 0> { + template + static constexpr void prod(std::array& res, + const std::array& a, + const std::array& b) { + std::get<0>(res) += a[0] * b[0]; + } +}; +} // namespace detail + /// Product of polynomials template constexpr auto operator*(const Polynomial& p1, const Polynomial& p2) { Polynomial() * std::declval()), o1 + o2> res; - for (UInt i = 0; i < p1.terms; ++i) - for (UInt j = 0; j < p2.terms; ++j) - res.coeffs[i + j] += p1.coeffs[i] * p2.coeffs[j]; + detail::coeff_product::prod(res.coeffs, p1.coeffs, + p2.coeffs); return res; } +namespace detail { +template +struct coeff_sum { + template + static constexpr void sum(std::array& res, + const std::array& a, + const std::array& b) { + std::get(res) += ((i < m) ? a[i] : 0) + ((i < l) ? b[i] : 0); + coeff_sum::sum(res, a, b); + } +}; + +template <> +struct coeff_sum<0> { + template + static constexpr void sum(std::array& res, + const std::array& a, + const std::array& b) { + std::get<0>(res) += a[0] + b[0]; + } +}; +} // namespace detail + /// Sum of polynomials template constexpr auto operator+(const Polynomial& p1, const Polynomial& p2) { Polynomial() + std::declval()), std::max(o1, o2)> res; - for (UInt i = 0; i < res.terms; ++i) - res.coeffs[i] += ((i < p1.terms) ? p1.coeffs[i] : 0) + - ((i < p2.terms) ? p2.coeffs[i] : 0); + detail::coeff_sum::sum(res.coeffs, p1.coeffs, p2.coeffs); return res; } /* -------------------------------------------------------------------------- */ /* Simplification rules */ /* -------------------------------------------------------------------------- */ #define POLY_SIMPLIFICATION(opsign, std_op) \ template \ constexpr auto operator opsign( \ const Polynomial& p1, \ const BinaryOperation, Derived, std_op>& p2) { \ return p2.op(p2.op(p1, p2.operands.first), p2.operands.second); \ } \ \ template \ constexpr auto operator opsign( \ const BinaryOperation, Derived, std_op>& p2, \ const Polynomial& p1) { \ return p2.op(p1, p2); \ } \ \ template \ constexpr auto operator opsign( \ const Polynomial& p1, \ const SBinaryOperation, Derived, std_op>& p2) { \ return p2.op(p1, p2.commute()); \ } \ \ template \ constexpr auto operator opsign( \ const SBinaryOperation, Derived, std_op>& p2, \ const Polynomial& p1) { \ return p2.op(p1, p2.commute()); \ } -POLY_SIMPLIFICATION(+, std::plus<>); -POLY_SIMPLIFICATION(*, std::multiplies<>); +POLY_SIMPLIFICATION(+, std::plus<>) +POLY_SIMPLIFICATION(*, std::multiplies<>) #undef POLY_SIMPLIFICATION template constexpr auto operator/(const Product, Derived>& p, - const Constant& c) { + const Constant& c) { return p.operands.second * (p.operands.first / c); } template constexpr auto operator/(const Product>& p, - const Constant& c) { + const Constant& c) { return p.operands.second * (p.operands.first / c); } template constexpr auto operator*(const Polynomial& p, - const Division>& d) { + const Division>& d) { return (p / d.operands.second) * d.operands.first; } template constexpr auto operator*(const Division>& d, const Polynomial& p) { return (p / d.operands.second) * d.operands.first; } /* -------------------------------------------------------------------------- */ /* Output */ /* -------------------------------------------------------------------------- */ template std::ostream& operator<<(std::ostream& os, const Polynomial& p) { os << p.coeffs[0]; if (p.terms >= 2) { os << " + " << p.coeffs[1] << "*z"; for (UInt i = 2; i < p.terms; ++i) os << " + " << p.coeffs[i] << "*z^" << i; } return os; } } // namespace expolit #endif diff --git a/tests/SConstruct b/tests/SConstruct index 2601785..ff4fe1b 100644 --- a/tests/SConstruct +++ b/tests/SConstruct @@ -1,7 +1,7 @@ import os -env = DefaultEnvironment(ENV=os.environ) -env.MergeFlags('-std=c++17 -g -O0') +env = DefaultEnvironment(ENV=os.environ, CXX='g++-6') +env.MergeFlags('-std=c++14 -g -O0 -Wall -Wextra -pedantic') env.Append(LIBS=['pthread', 'gtest', 'gtest_main'], CPPPATH=['../include']) env.Program('tests_gtest', ['tests_gtest.cc'])