Skip to content

Commit

Permalink
BROKEN: Massive rework of operators, variables, etc to support arbitr…
Browse files Browse the repository at this point in the history
…ary variable names, plus other improvements like a proper unaryop for negation. Fixed a number of issues, including scope of previous unary-, layout of code, separation of runtime and compile-time code so these can be properly tested. Current errors are to do with parsing not supporting the new variable type.
  • Loading branch information
toastedcrumpets committed Apr 27, 2021
1 parent 1a3ff23 commit 37fe732
Show file tree
Hide file tree
Showing 29 changed files with 576 additions and 448 deletions.
35 changes: 20 additions & 15 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,32 +128,37 @@ endif(DOXYGEN_FOUND)
######################################################################
######### TEST TARGETS
######################################################################
find_package(GTest REQUIRED)


function(stator_test name) #Registers a unit-test
add_executable(${name} ${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}.cpp)
target_include_directories(${name} PUBLIC ${GTEST_INCLUDE_DIRS})
target_link_libraries(${name} PUBLIC ${GTEST_LIBRARIES} pthread)
add_test(${name} ${name})
endfunction(stator_test)

add_executable(symbolic_example ${CMAKE_CURRENT_SOURCE_DIR}/examples/symbolic_example.cpp)
#add_executable(symbolic_example ${CMAKE_CURRENT_SOURCE_DIR}/examples/symbolic_example.cpp)

#stator_test(orphan_static_list)
stator_test(test_stack_vector)
stator_test(stack_vector_test)

if(CXX11_TEMPLATE_TYPEDEFS)
#stator_test(geometry_shapes)
stator_test(test_symbolic_generic)
stator_test(test_symbolic_polynomial)
stator_test(test_symbolic_poly_solve_roots)
stator_test(test_symbolic_poly_taylor)
stator_test(test_symbolic_runtime)
stator_test(test_symbolic_numeric)
stator_test(test_symbolic_parser)
stator_test(test_symbolic_ad)
#stator_test(test_symbolic_integration)
#stator_test(geometry_shapes_test)
stator_test(symbolic_generic_test)
stator_test(symbolic_polynomial_test)
stator_test(symbolic_poly_solve_roots_test)
stator_test(symbolic_poly_taylor_test)
stator_test(symbolic_runtime_test)
#stator_test(symbolic_numeric_test)
#stator_test(symbolic_parser_test)
#stator_test(symbolic_ad_test)
#stator_test(symbolic_integration_test)
endif()

######################################################################
########## PYTHON INTERFACE
######################################################################
add_subdirectory(extern/pybind11)
pybind11_add_module(core pysrc/stator/core.cpp)
target_include_directories(core PRIVATE ${PROJECT_SOURCE_DIR})
#add_subdirectory(extern/pybind11)
#pybind11_add_module(core pysrc/stator/core.cpp)
#target_include_directories(core PRIVATE ${PROJECT_SOURCE_DIR})
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[pytest]
norecursedirs = extern build
testpaths = tests build/temp.linux-x86_64-3.8/
cpp_files = *_test
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[aliases]
test=pytest
test=pytest
12 changes: 5 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from distutils.version import LooseVersion
from glob import glob

# Setup following advice from
# https://www.benjack.io/2017/06/12/python-cpp-tests.html


class CMakeExtension(Extension):
def __init__(self, name, sourcedir=''):
Expand Down Expand Up @@ -74,10 +77,7 @@ def run(self):
super(CatchTestCommand, self).run()
print("\nPython tests complete, now running C++ tests...\n")
# Run catch tests
subprocess.call(['./*_test'],
cwd=os.path.join('build',
self.distutils_dir_name('temp')),
shell=True)
subprocess.call(['ctest -j8 --output-on-failure'], cwd=os.path.join('build', self.distutils_dir_name('temp')), shell=True)



Expand All @@ -97,9 +97,7 @@ def run(self):
CMakeExtension('stator.core')
],
extras_require={"test": "pytest"},
# Currently, build_ext only provides an optional "highest supported C++
# level" feature, but in the future it may provide more features.
cmdclass=dict(build_ext=CMakeBuild),
cmdclass=dict(build_ext=CMakeBuild, test=CatchTestCommand),
setup_requires=['pytest-runner'],
tests_require=['pytest'],
zip_safe=False,
Expand Down
5 changes: 5 additions & 0 deletions stator/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#include <memory>

namespace stator {

/*! \brief A compile-time (constexpr) conversion from a number to a string.
*/


/*! \brief A C++ version of python's string trim function
\in str The string to be trimmed.
\in whitespace A string of characters to be treated as whitespace and trimmed from the start and end of the string.
Expand Down
63 changes: 51 additions & 12 deletions stator/symbolic/binary_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,6 @@ namespace sym {
static const bool value = IsSymbolic<LHS>::value || (!IsSymbolic<LHS>::value && IsSymbolic<RHS>::value);
};

/*! \brief Symbolic unary positive operator. */
template<class Arg,
typename = typename std::enable_if<IsSymbolic<Arg>::value>::type>
Arg operator+(const Arg& l) { return l; }

/*! \brief Symbolic unary negation operator. */
template<class Arg,
typename = typename std::enable_if<IsSymbolic<Arg>::value>::type>
auto operator-(const Arg& l) -> STATOR_AUTORETURN(C<-1>() * l)

/*! \brief Symbolic addition operator. */
template<class LHS, class RHS,
typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
Expand Down Expand Up @@ -378,6 +368,55 @@ namespace sym {
auto derivative(const ArrayOp<LHS, RHS>& f, Var v)
-> STATOR_AUTORETURN(f);
/*! \}*/
}


template<class LHS, class RHS, class Op, class Var, class Arg>
auto sub(BinaryOp<LHS, Op, RHS> f, EqualityOp<Var, Arg> x)
-> STATOR_AUTORETURN_BYVALUE(Op::apply(sub(f._l, x), sub(f._r, x)));

namespace detail {
/*! \brief Returns the binding powers (precedence) of binary operators.
As unary operators/tokens have no arguments which can be bound
by other operators, we return a large binding power (which
should exclude them from any binding power calculations).
*/
template<class T>
std::pair<int, int> BP (const T& v)
{ return std::make_pair(std::numeric_limits<int>::max(), std::numeric_limits<int>::max()); }

/*! \brief Returns the binding powers (precedence) of binary
operators (specialisation for binary ops).
*/
template<class LHS, class Op, class RHS>
std::pair<int, int> BP (const sym::BinaryOp<LHS, Op, RHS>& v) {
const int L = Op::leftBindingPower;
const int R = sym::detail::RBP<Op>();
return std::make_pair(L, R);
}

template<class Config>
std::string paren_wrap(std::string arg) {
return ((Config::Latex_output) ? "\\left(" : "(") + arg + ((Config::Latex_output) ? "\\right)" : ")");
}
}

template<class Config = DefaultReprConfig>
inline std::string repr(const sym::detail::Multiply& op) {
return (Config::Latex_output) ? "*" : "\\times ";
}

template<class Config = DefaultReprConfig, class LHS, class RHS, class Op>
inline std::string repr(const sym::BinaryOp<LHS, Op, RHS>& op) {
const auto this_BP = detail::BP(op);
const auto LHS_BP = detail::BP(op._l);
const auto RHS_BP = detail::BP(op._r);

std::string LHS_repr = repr<Config>(op._l);
if (LHS_BP.second < this_BP.first || Config::Force_parenthesis) LHS_repr = detail::paren_wrap<Config>(LHS_repr);

std::string RHS_repr = repr<Config>(op._r);
if (this_BP.second > RHS_BP.first || Config::Force_parenthesis) RHS_repr = detail::paren_wrap<Config>(RHS_repr);

return (Config::Latex_output ? Op::l_latex_repr() : Op::l_repr()) + LHS_repr + (Config::Latex_output ? Op::latex_repr() : Op::repr()) + RHS_repr + (Config::Latex_output ? Op::r_latex_repr() : Op::r_repr());
}
}
17 changes: 16 additions & 1 deletion stator/symbolic/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ namespace sym {
typedef C<1, 1> value;
};

template<std::intmax_t n1, std::intmax_t d1>
constexpr auto operator-(C<n1, d1>) -> STATOR_AUTORETURN((C<-n1, d1>()));

template<std::intmax_t n1, std::intmax_t d1>
constexpr auto operator+(C<n1, d1>) -> STATOR_AUTORETURN((C<n1, d1>()));

template<std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2>
constexpr auto operator+(C<n1, d1>, C<n2, d2>) -> STATOR_AUTORETURN((typename detail::C_wrap<std::ratio_add<std::ratio<n1,d1>, std::ratio<n2,d2> > >::type()));

Expand All @@ -120,7 +126,7 @@ namespace sym {
template<std::intmax_t n1, std::intmax_t d1, std::intmax_t n2, std::intmax_t d2>
constexpr bool operator==(const C<n1, d1>&, const C<n2, d2>&)
{ return std::ratio_equal<std::ratio<n1, d1>, std::ratio<n2, d2> >::value; }

template<class T, typename = typename std::enable_if<!is_C<T>::value>::type>
T operator+(const T& l, Null) { return l; }
template<class T, typename = typename std::enable_if<!is_C<T>::value>::type>
Expand Down Expand Up @@ -202,4 +208,13 @@ namespace sym {
struct IsConstant<std::complex<T> > : IsConstant<T> {};

}// namespace detail

template<class Config = DefaultReprConfig, std::intmax_t Num, std::intmax_t Denom>
inline std::string repr(const sym::C<Num, Denom>)
{
if (Config::Debug_output)
return "C<" + repr(Num) + ((Denom!=1) ? (std::string(", ") + repr(Denom) + ">") : std::string(">"));
else
return (Denom!=1) ? ("("+repr(Num)+"/"+repr(Denom)+")") : repr(Num);
}
}
2 changes: 1 addition & 1 deletion stator/symbolic/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ namespace sym {

tk.expect(":");
Expr value = tk.parseExpression(BP());
a[key.getID()] = value;
a[key] = value;
if (tk.next() == "}") break;
tk.expect(",");
}
Expand Down
44 changes: 43 additions & 1 deletion stator/symbolic/polynomial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <tuple>

namespace sym {
//The default polynomial
template<size_t Order, class Real = double, class PolyVar = Var<>> class Polynomial;

template<size_t Order, std::intmax_t num, std::intmax_t denom, class PolyVar>
Expand Down Expand Up @@ -184,6 +185,16 @@ namespace sym {
sub(const Polynomial<Order, Coeff_t, Var1>& f, const EqualityOp<Var2, Var<VarArgs...> >& x_container)
{ return Polynomial<Order, Coeff_t, Var<VarArgs...> >(f.begin(), f.end()); }


/*! \brief Substitution when polynomial is not in the correct variable.
*/
template<class Coeff_t, size_t Order, class Var1, class Var2, class SUB,
typename = typename enable_if_var_not_in<Var2, Var1>::type>
Polynomial<Order, Coeff_t, Var1>
sub(const Polynomial<Order, Coeff_t, Var1>& f, const EqualityOp<Var2, SUB>& x_container)
{ return f; }


/*! \brief Optimised Polynomial substitution for Null
insertions. */
template<size_t Order, class Coeff_t, class PolyVar, class SubVar,
Expand Down Expand Up @@ -1874,4 +1885,35 @@ namespace sym {
return os;
}
}
} // namespace sym

/*! \relates Polynomial
\name Polynomial input/output operations
\{
*/
/*! \brief Returns a human-readable representation of the Polynomial. */
template<class Config = DefaultReprConfig, class Coeff_t, size_t N, class PolyVar>
inline std::string repr(const sym::Polynomial<N, Coeff_t, PolyVar>& poly) {
std::ostringstream oss;
size_t terms = 0;
oss << "P(";
for (size_t i(N); i != 0; --i) {
if (poly[i] == sym::empty_sum(poly[i])) continue;
if (terms != 0)
oss << " + ";
++terms;
oss << repr<Config>(poly[i]) << "*" << PolyVar::getName();
if (i > 1)
oss << "^" << i;
}
if ((poly[0] != sym::empty_sum(poly[0])) || (terms == 0)) {
if (terms != 0)
oss << " + ";
++terms;
oss << repr<Config>(poly[0]);
}
oss << ")";
return oss.str();
}
/*! \} */
}

Loading

0 comments on commit 37fe732

Please sign in to comment.