From eb5512ea0fde2aea82ac2523d9f8d4fc37e0e1d8 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Wed, 1 Oct 2025 18:00:47 +0200 Subject: [PATCH 1/4] [actions] OctaSym can now be involved in analytic expressions + binding + tests --- python/src/core/actions/codac2_py_OctaSym.cpp | 6 ++++++ src/core/CMakeLists.txt | 1 + src/core/actions/codac2_OctaSym.cpp | 4 ++-- src/core/actions/codac2_OctaSym.h | 20 +++++++++++++------ src/core/tools/codac2_math.h | 6 ++++-- tests/core/actions/codac2_tests_OctaSym.cpp | 11 ++++++++++ tests/core/actions/codac2_tests_OctaSym.py | 9 +++++++++ 7 files changed, 47 insertions(+), 10 deletions(-) diff --git a/python/src/core/actions/codac2_py_OctaSym.cpp b/python/src/core/actions/codac2_py_OctaSym.cpp index 3d216ee1f..ad458ddef 100644 --- a/python/src/core/actions/codac2_py_OctaSym.cpp +++ b/python/src/core/actions/codac2_py_OctaSym.cpp @@ -14,10 +14,12 @@ #include "codac2_py_Ctc.h" #include "codac2_py_Sep.h" #include +#include #include #include #include #include "codac2_py_OctaSym_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac2_py_cast.h" using namespace std; using namespace codac2; @@ -75,6 +77,10 @@ void export_OctaSym(py::module& m) SAMPLEDTRAJ_T_OCTASYM_OPERATORCALL_CONST_SAMPLEDTRAJ_T_REF_CONST, "x"_a) + .def("__call__", [](const OctaSym& a, const VectorExpr& x) { return a.operator()(x); }, + VECTOREXPR_OCTASYM_OPERATORCALL_CONST_V_REF_CONST, + "x"_a) + .def("__repr__", [](const OctaSym& s) { std::ostringstream stream; stream << s; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 896916c30..dfad84d20 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,6 +6,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/actions/codac2_OctaSym.cpp ${CMAKE_CURRENT_SOURCE_DIR}/actions/codac2_OctaSym.h + ${CMAKE_CURRENT_SOURCE_DIR}/actions/codac2_OctaSym_operator.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_Ctc.h ${CMAKE_CURRENT_SOURCE_DIR}/contractors/codac2_CtcAction.cpp diff --git a/src/core/actions/codac2_OctaSym.cpp b/src/core/actions/codac2_OctaSym.cpp index e29be8b8e..80d961498 100644 --- a/src/core/actions/codac2_OctaSym.cpp +++ b/src/core/actions/codac2_OctaSym.cpp @@ -41,7 +41,7 @@ OctaSym OctaSym::operator*(const OctaSym& s) const assert_release(size() == s.size()); OctaSym a(*this); for(size_t i = 0 ; i < a.size() ; i++) - a[i] = _sign(s[i])*(*this)[std::abs((int)s[i])-1]; + a[i] = sign(s[i])*(*this)[std::abs((int)s[i])-1]; return a; } @@ -49,6 +49,6 @@ Matrix OctaSym::permutation_matrix() const { Matrix m = Matrix::zero(size(),size()); for(size_t i = 0 ; i < size() ; i++) - m(i,std::abs((*this)[i])-1) = _sign((*this)[i]); + m(i,std::abs((*this)[i])-1) = sign((*this)[i]); return m; } \ No newline at end of file diff --git a/src/core/actions/codac2_OctaSym.h b/src/core/actions/codac2_OctaSym.h index a4bc793f8..0e4125c44 100644 --- a/src/core/actions/codac2_OctaSym.h +++ b/src/core/actions/codac2_OctaSym.h @@ -23,6 +23,7 @@ namespace codac2 class SepBase; class SepAction; class SetExpr; + class OctaSymOp; /** * \class Action @@ -46,11 +47,6 @@ namespace codac2 Matrix permutation_matrix() const; - int _sign(int a) const - { - return (a > 0) ? 1 : ((a < 0) ? -1 : 0); - } - template requires (Derived::ColsAtCompileTime == 1) Mat operator()(const Eigen::MatrixBase& x) const @@ -58,7 +54,7 @@ namespace codac2 assert_release(x.size() == (Index)size()); Mat x_(x); for(size_t i = 0 ; i < size() ; i++) - x_[i] = _sign((*this)[i])*x[std::abs((*this)[i])-1]; + x_[i] = sign((*this)[i])*x[std::abs((*this)[i])-1]; return x_; } @@ -84,6 +80,18 @@ namespace codac2 return y; } + template + // To avoid ambiguity with operator()(const Eigen::MatrixBase& x): + requires (std::is_same_v || std::is_same_v) + inline VectorExpr operator()(const V& x1) const + { + if constexpr(std::is_same_v) + assert_release((Index)this->size() == x1->output_shape().first); + else + assert_release((Index)this->size() == x1.output_shape().first); + return { std::make_shared>(*this, x1) }; + } + friend std::ostream& operator<<(std::ostream& str, const OctaSym& s) { str << "("; diff --git a/src/core/tools/codac2_math.h b/src/core/tools/codac2_math.h index 6739176de..b574c76de 100644 --- a/src/core/tools/codac2_math.h +++ b/src/core/tools/codac2_math.h @@ -30,9 +30,11 @@ namespace codac2 constexpr double PI = std::numbers::pi; // Need C++20 - inline int sign(double x) + template + requires std::is_arithmetic_v + inline constexpr int sign(T x) { - return (x > 0) ? 1 : ((x < 0) ? -1 : 0); + return (x > T(0)) - (x < T(0)); } inline int integer(double x) diff --git a/tests/core/actions/codac2_tests_OctaSym.cpp b/tests/core/actions/codac2_tests_OctaSym.cpp index 024b18e03..e857dc2c2 100644 --- a/tests/core/actions/codac2_tests_OctaSym.cpp +++ b/tests/core/actions/codac2_tests_OctaSym.cpp @@ -9,6 +9,7 @@ #include #include +#include using namespace std; using namespace codac2; @@ -28,4 +29,14 @@ TEST_CASE("OctaSym") OctaSym c({-2,1,3}); CHECK(c.permutation_matrix() == Matrix({{0,-1,0},{1,0,0},{0,0,1}})); +} + +TEST_CASE("OctaSym as operator") +{ + OctaSym a({3,1,-2}); + VectorVar x(3); + AnalyticFunction f({x}, a(2*x)); + CHECK(f.eval(IntervalVector({{1},{2},{3}})) == IntervalVector({{6},{2},{-4}})); + CHECK(f.eval(IntervalVector({{-oo,oo},{-oo,oo},{-oo,oo}})) == IntervalVector(3)); + CHECK(f.eval(IntervalVector::empty(3)) == IntervalVector::empty(3)); } \ No newline at end of file diff --git a/tests/core/actions/codac2_tests_OctaSym.py b/tests/core/actions/codac2_tests_OctaSym.py index 1f849ae09..4c03acbc2 100644 --- a/tests/core/actions/codac2_tests_OctaSym.py +++ b/tests/core/actions/codac2_tests_OctaSym.py @@ -28,5 +28,14 @@ def test_OctaSym(self): c = OctaSym([-2,1,3]) self.assertTrue(c.permutation_matrix() == Matrix([[0,-1,0],[1,0,0],[0,0,1]])) + def test_OctaSym_as_operator(self): + + a = OctaSym([3,1,-2]) + x = VectorVar(3) + f = AnalyticFunction([x], a(2*x)) + self.assertTrue(f.eval(IntervalVector([[1],[2],[3]])) == IntervalVector([[6],[2],[-4]])) + self.assertTrue(f.eval(IntervalVector([[-oo,oo],[-oo,oo],[-oo,oo]])) == IntervalVector(3)) + self.assertTrue(f.eval(IntervalVector.empty(3)) == IntervalVector.empty(3)) + if __name__ == '__main__': unittest.main() \ No newline at end of file From ab2e5a77d58e48f02697180f26662b8168c94c7a Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Wed, 1 Oct 2025 19:57:07 +0200 Subject: [PATCH 2/4] [actions] OctaSym can now be involved in analytic expressions + binding + tests (missing file) --- src/core/actions/codac2_OctaSym_operator.h | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/core/actions/codac2_OctaSym_operator.h diff --git a/src/core/actions/codac2_OctaSym_operator.h b/src/core/actions/codac2_OctaSym_operator.h new file mode 100644 index 000000000..18040dd18 --- /dev/null +++ b/src/core/actions/codac2_OctaSym_operator.h @@ -0,0 +1,129 @@ +/** + * \file codac2_OctaSym_operator.h + * ---------------------------------------------------------------------------- + * \date 2025 + * \author Simon Rohou + * \copyright Copyright 2025 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +#include "codac2_OctaSym.h" + +namespace codac2 +{ + struct OctaSymOp + { + template + static inline std::string str(const X1& x1) + { + return "sym(" + x1->str() + ")"; + } + + template + static inline std::pair output_shape([[maybe_unused]] const X1& s1) + { + return s1->output_shape(); + } + + static inline IntervalVector fwd(const OctaSym& s, const IntervalVector& x1) + { + assert((Index)s.size() == x1.size()); + return s(x1); + } + + static inline VectorType fwd_natural(const OctaSym& s, const VectorType& x1) + { + assert((Index)s.size() == x1.m.size()); + return { + fwd(s, x1.a), + x1.def_domain + }; + } + + static inline VectorType fwd_centered(const OctaSym& s, const VectorType& x1) + { + assert((Index)s.size() == x1.m.size()); + + auto da = x1.da; + for(size_t i = 0 ; i < s.size() ; i++) + da.row(i) = sign(s[i])*x1.da.row(std::abs(s[i])-1); + + return { + fwd(s, x1.m), + fwd(s, x1.a), + da, + x1.def_domain + }; + } + + static inline void bwd(const OctaSym& s, const IntervalVector& y, IntervalVector& x1) + { + assert((Index)s.size() == y.size() && (Index)s.size() == x1.size()); + x1 &= s.invert()(y); + } + }; + + + template<> + class AnalyticOperationExpr + : public AnalyticExpr, public OperationExprBase> + { + public: + + AnalyticOperationExpr(const OctaSym& s, const VectorExpr& x1) + : OperationExprBase>(x1), _s(s) + { } + + std::shared_ptr copy() const + { + return std::make_shared>(*this); + } + + void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) + { + return OperationExprBase>::replace_arg(old_arg_id, new_expr); + } + + VectorType fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const + { + if(natural_eval) + return AnalyticExpr::init_value( + v, OctaSymOp::fwd_natural(_s, std::get<0>(this->_x)->fwd_eval(v, total_input_size, natural_eval))); + else + return AnalyticExpr::init_value( + v, OctaSymOp::fwd_centered(_s, std::get<0>(this->_x)->fwd_eval(v, total_input_size, natural_eval))); + } + + void bwd_eval(ValuesMap& v) const + { + OctaSymOp::bwd(_s, AnalyticExpr::value(v).a, std::get<0>(this->_x)->value(v).a); + std::get<0>(this->_x)->bwd_eval(v); + } + + std::pair output_shape() const { + return { _s.size(), 1 }; + } + + virtual bool belongs_to_args_list(const FunctionArgsList& args) const + { + return std::get<0>(this->_x)->belongs_to_args_list(args); + } + + std::string str(bool in_parentheses = false) const + { + std::string s = "S"; // user cannot (yet) specify a name for the symmetry + return in_parentheses ? "(" + s + ")" : s; + } + + virtual bool is_str_leaf() const + { + return true; + } + + protected: + + const OctaSym _s; + }; +} \ No newline at end of file From 459fbe26c0ff340fcb576cf53815fba2730fae45 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Wed, 1 Oct 2025 20:20:04 +0200 Subject: [PATCH 3/4] [actions] corrected template error in OctaSym --- src/core/actions/codac2_OctaSym.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/actions/codac2_OctaSym.h b/src/core/actions/codac2_OctaSym.h index 0e4125c44..46a9f6037 100644 --- a/src/core/actions/codac2_OctaSym.h +++ b/src/core/actions/codac2_OctaSym.h @@ -82,7 +82,7 @@ namespace codac2 template // To avoid ambiguity with operator()(const Eigen::MatrixBase& x): - requires (std::is_same_v || std::is_same_v) + requires (std::is_same_v || std::is_same_v) inline VectorExpr operator()(const V& x1) const { if constexpr(std::is_same_v) From 862bcb0cb9c875d2a9ff6650807fbf546d140349 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Wed, 1 Oct 2025 21:43:05 +0200 Subject: [PATCH 4/4] [doc] updated manual: added analytic operators for actions, traj, tubes --- .../functions/analytic/analytic_operators.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/manual/manual/functions/analytic/analytic_operators.rst b/doc/manual/manual/functions/analytic/analytic_operators.rst index 89a9f8441..73519509f 100644 --- a/doc/manual/manual/functions/analytic/analytic_operators.rst +++ b/doc/manual/manual/functions/analytic/analytic_operators.rst @@ -139,6 +139,12 @@ When operators are available for operations 2--3, then an ``AnalyticFunction`` c .. |MatrixOp| replace:: :raw-html:`MatrixOp` +.. |OctaSymOp| replace:: :raw-html:`OctaSymOp` + +.. |TrajOp| replace:: :raw-html:`TrajectoryOp` + +.. |TubeOp| replace:: :raw-html:`TubeOp` + Only the :bg-ok:`✓` operators are supported at the moment. If you notice any mathematical operators missing from the list below, feel free to contribute to the library. You can submit your suggestions or pull requests on the `GitHub repository of Codac `_. @@ -266,6 +272,14 @@ If you notice any mathematical operators missing from the list below, feel free +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ | :math:`\left(\mathbf{x}_1,\mathbf{x}_2,\dots\right)`| ``mat(x1,x2,...)`` | |MatrixOp| | ``x1``, ``...``: vector ||okk| ||okk| ||okk| ||nok| | +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ + | :math:`\sigma(\mathbf{x})` | ``s(x)`` | |OctaSymOp| | ``s``: action, ``x``: vector ||okk| ||okk| ||okk| ||okk| | + +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ + | :bg-title:`Temporal operations` | + +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ + | :math:`\mathbf{x}(t)` | ``x(t)`` | |TrajOp| | ``x``: trajectory, ``t``: scalar ||okk| ||nok| ||nok| ||nok| | + +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ + | :math:`[\mathbf{x}](t)` | ``x(t)`` | |TubeOp| | ``x``: tube, ``t``: scalar ||okk| ||nok| ||nok| ||nok| | + +-----------------------------------------------------+----------------------+---------------+-------------------------------------+--------+--------+-------+------------+ | Note: the operator :math:`\det` is only available for :math:`1\times 1` and :math:`2\times 2` matrices. | Note: the operator :math:`\bmod` is only available for real periods (double precision), interval periods are not yet supported.