diff --git a/python/src/core/codac2_py_core.cpp b/python/src/core/codac2_py_core.cpp index afe9b27f3..9183b93eb 100644 --- a/python/src/core/codac2_py_core.cpp +++ b/python/src/core/codac2_py_core.cpp @@ -179,6 +179,7 @@ PYBIND11_MODULE(_core, m) m.doc() = string(FOR_MATLAB ? "Matlab" : "Python") + " binding of Codac (core)"; m.attr("oo") = oo; m.attr("PI") = PI; + m.attr("FOR_MATLAB") = FOR_MATLAB; export_TimePropag(m); diff --git a/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp b/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp index bb037bd37..169546a64 100644 --- a/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp +++ b/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp @@ -194,6 +194,23 @@ namespace SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_INTERVAL_REF, py::return_value_policy::reference_internal, py::is_operator()); + + if constexpr(!FOR_MATLAB && std::is_same_v) + { + pyclass + .def("__matmul__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator*, + SLICEDTUBE_INTERVALMATRIX_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALMATRIX_REF, + py::is_operator()) + + .def("__matmul__", (SlicedTube(*)(const SlicedTube&,const T&)) &codac2::operator*, + SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, + py::is_operator()) + + .def("__rmatmul__", + [](const SlicedTube& x2, const T& x1) { return x1 * x2; }, + SLICEDTUBE_T_OPERATORMUL_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()); + } } } @@ -401,6 +418,14 @@ void export_SlicedTube_operations( SLICEDTUBE_INTERVALVECTOR_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALVECTOR_REF, py::is_operator()); + if constexpr(!FOR_MATLAB) + { + py_SlicedTube_IntervalMatrix + .def("__matmul__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator*, + SLICEDTUBE_INTERVALVECTOR_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALVECTOR_REF, + py::is_operator()); + } + bind_scalar_slicedtube_functions(m); py_SlicedTube_Interval diff --git a/python/src/core/functions/analytic/codac2_py_AnalyticExprWrapper.h b/python/src/core/functions/analytic/codac2_py_AnalyticExprWrapper.h index 652e398ef..0a161f0bb 100644 --- a/python/src/core/functions/analytic/codac2_py_AnalyticExprWrapper.h +++ b/python/src/core/functions/analytic/codac2_py_AnalyticExprWrapper.h @@ -129,6 +129,13 @@ inline void export_VectorExpr(py::module& m) ; + if constexpr(!FOR_MATLAB) + { + exported + .def("__rmatmul__", [](const VectorExpr& e1, const MatrixExpr& e2) { return e2*e1; }, py::is_operator()) + ; + } + py::implicitly_convertible(); py::implicitly_convertible(); py::implicitly_convertible(); @@ -181,6 +188,20 @@ inline void export_MatrixExpr(py::module& m) ; + if constexpr(!FOR_MATLAB) + { + exported + .def("__matmul__", [](const MatrixExpr& e1, const MatrixExpr& e2) { return e1*e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixExpr& e1, const IntervalMatrix& e2) { return e1*e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixExpr& e1, const MatrixVar& e2) { return e1*e2; }, py::is_operator()) + .def("__rmatmul__", [](const MatrixExpr& e1, const IntervalMatrix& e2) { return e2*e1; }, py::is_operator()) + .def("__rmatmul__", [](const MatrixExpr& e1, const MatrixVar& e2) { return e2*e1; }, py::is_operator()) + .def("__matmul__", [](const MatrixExpr& e1, const VectorVar& e2) { return e1*e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixExpr& e1, const IntervalVector& e2) { return e1*e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixExpr& e1, const VectorExpr& e2) { return e1*e2; }, py::is_operator()) + ; + } + py::implicitly_convertible(); py::implicitly_convertible(); py::implicitly_convertible(); diff --git a/python/src/core/functions/analytic/codac2_py_analytic_variables.cpp b/python/src/core/functions/analytic/codac2_py_analytic_variables.cpp index ff5ef04ef..16edf41cc 100644 --- a/python/src/core/functions/analytic/codac2_py_analytic_variables.cpp +++ b/python/src/core/functions/analytic/codac2_py_analytic_variables.cpp @@ -161,6 +161,14 @@ void export_VectorVar(py::module& m) .def("__truediv__", [](const VectorVar& e1, const ScalarExpr& e2) { return e1 / e2; }, py::is_operator()) .def("__truediv__", [](const VectorVar& e1, const Interval& e2) { return e1 / e2; }, py::is_operator()) ; + + if constexpr(!FOR_MATLAB) + { + exported + .def("__rmatmul__", [](const VectorVar& e1, const IntervalMatrix& e2) { return e2 * e1; }, py::is_operator()) + .def("__rmatmul__", [](const VectorVar& e1, const MatrixExpr& e2) { return e2 * e1; }, py::is_operator()) + ; + } py::implicitly_convertible(); } @@ -227,6 +235,20 @@ void export_MatrixVar(py::module& m) .def("__truediv__", [](const MatrixVar& e1, const ScalarExpr& e2) { return e1 / e2; }, py::is_operator()) .def("__truediv__", [](const MatrixVar& e1, const Interval& e2) { return e1 / e2; }, py::is_operator()) ; + + if constexpr(!FOR_MATLAB) + { + exported + .def("__matmul__", [](const MatrixVar& e1, const VectorVar& e2) { return e1 * e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixVar& e1, const VectorExpr& e2) { return e1 * e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixVar& e1, const IntervalVector& e2) { return e1 * e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixVar& e1, const MatrixVar& e2) { return e1 * e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixVar& e1, const MatrixExpr& e2) { return e1 * e2; }, py::is_operator()) + .def("__matmul__", [](const MatrixVar& e1, const IntervalMatrix& e2) { return e1 * e2; }, py::is_operator()) + .def("__rmatmul__", [](const MatrixVar& e1, const IntervalMatrix& e2) { return e2 * e1; }, py::is_operator()) + .def("__rmatmul__", [](const MatrixVar& e1, const MatrixExpr& e2) { return e2 * e1; }, py::is_operator()) + ; + } py::implicitly_convertible(); } \ No newline at end of file diff --git a/python/src/core/matrices/codac2_py_arithmetic_mul.cpp b/python/src/core/matrices/codac2_py_arithmetic_mul.cpp index 12f56a671..a3f05ccf3 100644 --- a/python/src/core/matrices/codac2_py_arithmetic_mul.cpp +++ b/python/src/core/matrices/codac2_py_arithmetic_mul.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "codac2_py_matlab.h" using namespace std; using namespace codac2; @@ -153,4 +154,49 @@ void export_arithmetic_mul( py_IM.def("__imul__", [](IntervalMatrix& x1, const IntervalMatrix& x2) { return x1*=x2; }, py::is_operator()); py_IM.def("__imul__", [](IntervalMatrix& x1, const IB& x2) { return x1*=x2; }, py::is_operator()); + // ====== Matrix products with the @ operator (not available in Matlab) + + if constexpr(!FOR_MATLAB) + { + //Vector operator*(const M& x1, const Vector& x2) + py_M.def("__matmul__", [](const Matrix& x1, const Vector& x2) -> Vector { return x1*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const Vector& x2) -> Vector { return x1*x2; }, py::is_operator()); + + //Matrix operator*(const M& x1, const M_& x2) + py_M.def("__matmul__", [](const Matrix& x1, const Matrix& x2) -> Matrix { return x1*x2; }, py::is_operator()); + py_M.def("__matmul__", [](const Matrix& x1, const B& x2) -> Matrix { return x1*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const Matrix& x2) -> Matrix { return x1*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const B& x2) -> Matrix { return x1*x2; }, py::is_operator()); + + //IntervalVector operator*(const M& x1, const IntervalVector& x2) + py_M.def("__matmul__", [](const Matrix& x1, const IntervalVector& x2) -> IntervalVector { return x1*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const IntervalVector& x2) -> IntervalVector { return x1*x2; }, py::is_operator()); + + //IntervalMatrix operator*(const M& x1, const IM& x2) + py_M.def("__matmul__", [](const Matrix& x1, const IntervalMatrix& x2) -> IntervalMatrix { return x1.template cast()*x2; }, py::is_operator()); + py_M.def("__matmul__", [](const Matrix& x1, const IB& x2) -> IntervalMatrix { return x1.template cast()*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const IntervalMatrix& x2) -> IntervalMatrix { return x1.template cast()*x2; }, py::is_operator()); + py_B.def("__matmul__", [](const B& x1, const IB& x2) -> IntervalMatrix { return x1.template cast()*x2; }, py::is_operator()); + + //IntervalVector operator*(const IM& x1, const Vector& x2) + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const Vector& x2) -> IntervalVector { return x1*x2.template cast(); }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const Vector& x2) -> IntervalVector { return x1*x2.template cast(); }, py::is_operator()); + + //IntervalMatrix operator*(const IM& x1, const M& x2) + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const Matrix& x2) -> IntervalMatrix { return x1*x2.template cast(); }, py::is_operator()); + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const B& x2) -> IntervalMatrix { return x1*x2.template cast(); }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const Matrix& x2) -> IntervalMatrix { return x1*x2.template cast(); }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const B& x2) -> IntervalMatrix { return x1*x2.template cast(); }, py::is_operator()); + + //IntervalVector operator*(const IM& x1, const IntervalVector& x2) + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const IntervalVector& x2) -> IntervalVector { return x1*x2; }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const IntervalVector& x2) -> IntervalVector { return x1*x2; }, py::is_operator()); + + //IntervalMatrix operator*(const IM& x1, const IM_& x2) + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const IntervalMatrix& x2) -> IntervalMatrix { return x1*x2; }, py::is_operator()); + py_IM.def("__matmul__", [](const IntervalMatrix& x1, const IB& x2) -> IntervalMatrix { return x1*x2; }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const IntervalMatrix& x2) -> IntervalMatrix { return x1*x2; }, py::is_operator()); + py_IB.def("__matmul__", [](const IB& x1, const IB& x2) -> IntervalMatrix { return x1*x2; }, py::is_operator()); + } + } \ No newline at end of file diff --git a/python/src/core/tools/codac2_py_transformations.cpp b/python/src/core/tools/codac2_py_transformations.cpp index 7be3fae73..8f09c1846 100644 --- a/python/src/core/tools/codac2_py_transformations.cpp +++ b/python/src/core/tools/codac2_py_transformations.cpp @@ -15,6 +15,7 @@ #include #include "codac2_py_transformations_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): #include "codac2_py_doc.h" +#include "codac2_py_matlab.h" using namespace std; using namespace codac2; @@ -41,6 +42,21 @@ void export_transformations(py::module& m) py::is_operator()) ; + + if constexpr(!FOR_MATLAB) + { + exported_affine2d + + .def("__matmul__", [](const Eigen::Affine2d& x1, const Vector& x2) + { + assert_release(x2.size() == 2); + return Vector(x1*Eigen::Vector2d(x2)); + }, + DOC_TO_BE_DEFINED, + py::is_operator()) + + ; + } m diff --git a/python/src/core/trajectory/codac2_py_SampledTraj.cpp b/python/src/core/trajectory/codac2_py_SampledTraj.cpp index 546aeced3..43ff82172 100644 --- a/python/src/core/trajectory/codac2_py_SampledTraj.cpp +++ b/python/src/core/trajectory/codac2_py_SampledTraj.cpp @@ -236,8 +236,12 @@ void add_traj_operators(py::class_>& pyclass) .def("__rmul__", [](const SampledTraj& x2, double x1) { return x1*x2; }, SAMPLEDTRAJ_T_OPERATORMUL_DOUBLE_CONST_SAMPLEDTRAJ_T_REF, py::is_operator()) + + .def("__mul__", [](const SampledTraj& x1, const T& x2) { return x1*x2; }, + SAMPLEDTRAJ_T_OPERATORMUL_CONST_SAMPLEDTRAJ_T_REF_CONST_Q_REF, + py::is_operator()) - .def("__mul__", [](const T& x1, const SampledTraj& x2) { return x1*x2; }, + .def("__rmul__", [](const SampledTraj& x2, const T& x1) { return x1*x2; }, SAMPLEDTRAJ_T_OPERATORMUL_CONST_Q_REF_CONST_SAMPLEDTRAJ_T_REF, py::is_operator()) @@ -288,6 +292,25 @@ void export_SampledTraj(py::module& m) ; + if constexpr(!FOR_MATLAB) + { + py_SampledTraj_Matrix + + .def("__matmul__", [](const SampledTraj& x1, const SampledTraj& x2) { return x1*x2; }, + SAMPLEDTRAJ_VECTOR_OPERATORMUL_CONST_SAMPLEDTRAJ_MATRIX_REF_CONST_SAMPLEDTRAJ_VECTOR_REF, + py::is_operator()) + + .def("__matmul__", [](const SampledTraj& x1, const SampledTraj& x2) { return x1*x2; }, + SAMPLEDTRAJ_T_OPERATORMUL_CONST_SAMPLEDTRAJ_T_REF_CONST_SAMPLEDTRAJ_T_REF, + py::is_operator()) + + .def("__rmatmul__", [](const SampledTraj& x2, const Matrix& x1) { return x1*x2; }, + SAMPLEDTRAJ_T_OPERATORMUL_CONST_Q_REF_CONST_SAMPLEDTRAJ_T_REF, + py::is_operator()) + + ; + } + m .def("sqr", (SampledTraj (*)(const SampledTraj&)) &codac2::sqr, diff --git a/tests/core/domains/tube/codac2_tests_SlicedTube.py b/tests/core/domains/tube/codac2_tests_SlicedTube.py index 8b0e39a51..f7854adbf 100644 --- a/tests/core/domains/tube/codac2_tests_SlicedTube.py +++ b/tests/core/domains/tube/codac2_tests_SlicedTube.py @@ -557,6 +557,24 @@ def test_inversion_vector_tube(self): inv = x.invert(inv_val, restricted) self.assertTrue(inv == Interval(15.2,38)) + @unittest.skipIf(FOR_MATLAB, "the @ operator is not available in Matlab") + def test_matrix_tube_matmul_operator(self): + + tdomain = create_tdomain(Interval(0,1), 0.5, False) + M,N = IntervalMatrix([[1,2],[3,4]]),IntervalMatrix([[0,1],[1,0]]) + A = SlicedTube(tdomain, M) + B = SlicedTube(tdomain, N) + x = SlicedTube(tdomain, IntervalVector([5,6])) + + self.assertTrue((A@B).codomain() == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue((A@N).codomain() == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue((M@B).codomain() == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue((Matrix([[1,2],[3,4]])@B).codomain() == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue((A@x).codomain() == IntervalVector([17,39])) + self.assertTrue((A@x).codomain() == (A*x).codomain()) + with self.assertRaises(TypeError): + x@x + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/tests/core/functions/analytic/codac2_tests_AnalyticFunction.py b/tests/core/functions/analytic/codac2_tests_AnalyticFunction.py index 7704aa720..afa5243e4 100644 --- a/tests/core/functions/analytic/codac2_tests_AnalyticFunction.py +++ b/tests/core/functions/analytic/codac2_tests_AnalyticFunction.py @@ -437,6 +437,53 @@ def test_eval(i,f,*args): self.assertTrue(f.eval(Interval(0.0,4.0))==Interval(-4.0,4.0)) self.assertTrue(f.eval(Interval(0.0))==Interval(0.0)) + @unittest.skipIf(FOR_MATLAB, "the @ operator is not available in Matlab") + def test_AnalyticFunction_matmul_operator(self): + + A,B = MatrixVar(2,2),MatrixVar(2,2) + x = VectorVar(2) + M,N = Matrix([[1,2],[3,4]]),Matrix([[0,1],[1,0]]) + v = Vector([5,6]) + + # eval() takes arguments of a single type: the matrix argument is set by composition + eval_Mv = lambda e: AnalyticFunction([x], AnalyticFunction([A,x], e)(M,x)).eval(v) + + # MatrixVar @ ... + self.assertTrue(AnalyticFunction([A,B], A@B).eval(M,N) == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue(AnalyticFunction([A,B], A@(2*B)).eval(M,N) == IntervalMatrix([[4,2],[8,6]])) + self.assertTrue(AnalyticFunction([A], A@IntervalMatrix(N)).eval(M) == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue(eval_Mv(A@x) == IntervalVector([17,39])) + self.assertTrue(eval_Mv(A@(2*x)) == IntervalVector([34,78])) + self.assertTrue(AnalyticFunction([A], A@IntervalVector(v)).eval(M) == IntervalVector([17,39])) + self.assertTrue(AnalyticFunction([B], IntervalMatrix(M)@B).eval(N) == IntervalMatrix([[2,1],[4,3]])) + self.assertTrue(AnalyticFunction([B], M@B).eval(N) == IntervalMatrix([[2,1],[4,3]])) + + # MatrixExpr @ ... + self.assertTrue(AnalyticFunction([A,B], (2*A)@(2*B)).eval(M,N) == IntervalMatrix([[8,4],[16,12]])) + self.assertTrue(AnalyticFunction([A,B], (2*A)@B).eval(M,N) == IntervalMatrix([[4,2],[8,6]])) + self.assertTrue(AnalyticFunction([A], (2*A)@IntervalMatrix(N)).eval(M) == IntervalMatrix([[4,2],[8,6]])) + self.assertTrue(eval_Mv((2*A)@x) == IntervalVector([34,78])) + self.assertTrue(eval_Mv((2*A)@(2*x)) == IntervalVector([68,156])) + self.assertTrue(AnalyticFunction([A], (2*A)@IntervalVector(v)).eval(M) == IntervalVector([34,78])) + self.assertTrue(AnalyticFunction([B], IntervalMatrix(M)@(2*B)).eval(N) == IntervalMatrix([[4,2],[8,6]])) + + # ... @ VectorVar, ... @ VectorExpr + self.assertTrue(AnalyticFunction([x], IntervalMatrix(M)@x).eval(v) == IntervalVector([17,39])) + self.assertTrue(AnalyticFunction([x], M@x).eval(v) == IntervalVector([17,39])) + self.assertTrue(AnalyticFunction([x], IntervalMatrix(M)@(2*x)).eval(v) == IntervalVector([34,78])) + + # Same results as with the * operator + h = AnalyticFunction([A], A@A) + f = AnalyticFunction([x,A], h(A)@x) + g = AnalyticFunction([x], f(x,Matrix([[0,2],[-1,0]]))) + self.assertTrue(g.eval(IntervalVector([[-1,1],[2,3]])) == IntervalVector([[-2,2],[-6,-4]])) + + # Matrix product only: no @ with scalar expressions + with self.assertRaises(TypeError): + A@ScalarVar() + with self.assertRaises(TypeError): + x@x + if __name__ == '__main__': unittest.main() diff --git a/tests/core/matrices/codac2_tests_arithmetic_mul.py b/tests/core/matrices/codac2_tests_arithmetic_mul.py index 2b14004b3..6fd387f15 100644 --- a/tests/core/matrices/codac2_tests_arithmetic_mul.py +++ b/tests/core/matrices/codac2_tests_arithmetic_mul.py @@ -110,5 +110,56 @@ def test_ArithmeticMul(self): self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]])*IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2)*IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) + @unittest.skipIf(FOR_MATLAB, "the @ operator is not available in Matlab") + def test_ArithmeticMul_matmul_operator(self): + + # Vector operator*(const M& x1, const Vector& x2) + self.assertTrue(Matrix([[1,2],[3,4]])@Vector([5,6]) == Vector([17,39])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@Vector([5,6]) == Vector([17,39])) + + # Matrix operator*(const M& x1, const M_& x2) + self.assertTrue(Matrix([[1,2],[3,4]])@Matrix([[5,6],[7,8]]) == Matrix([[19,22],[43,50]])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@Matrix([[5,6],[7,8]]) == Matrix([[19,22],[43,50]])) + self.assertTrue(Matrix([[1,2],[3,4]])@Matrix([[5,6],[7,8]]).block(0,0,2,2) == Matrix([[19,22],[43,50]])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@Matrix([[5,6],[7,8]]).block(0,0,2,2) == Matrix([[19,22],[43,50]])) + + # IntervalVector operator*(const M& x1, const IntervalVector& x2) + self.assertTrue(Matrix([[1,2],[3,4]])@IntervalVector([5,6]) == IntervalVector([17,39])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@IntervalVector([5,6]) == IntervalVector([17,39])) + + # IntervalMatrix operator*(const M& x1, const IM& x2) + self.assertTrue(Matrix([[1,2],[3,4]])@IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]) == IntervalMatrix([[[19,22],[22,25]],[[43,50],[50,57]]])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]) == IntervalMatrix([[[19,22],[22,25]],[[43,50],[50,57]]])) + self.assertTrue(Matrix([[1,2],[3,4]])@IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2) == IntervalMatrix([[[19,22],[22,25]],[[43,50],[50,57]]])) + self.assertTrue(Matrix([[1,2],[3,4]]).block(0,0,2,2)@IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2) == IntervalMatrix([[[19,22],[22,25]],[[43,50],[50,57]]])) + + # IntervalVector operator*(const IM& x1, const Vector& x2) + self.assertTrue(IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]])@Vector([5,6]) == IntervalVector([[17,28],[39,50]])) + self.assertTrue(IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2)@Vector([5,6]) == IntervalVector([[17,28],[39,50]])) + + # IntervalMatrix operator*(const IM& x1, const M& x2) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]])@Matrix([[1,2],[3,4]]) == IntervalMatrix([[[23,27],[34,40]],[[31,35],[46,52]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2)@Matrix([[1,2],[3,4]]) == IntervalMatrix([[[23,27],[34,40]],[[31,35],[46,52]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]])@Matrix([[1,2],[3,4]]).block(0,0,2,2) == IntervalMatrix([[[23,27],[34,40]],[[31,35],[46,52]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2)@Matrix([[1,2],[3,4]]).block(0,0,2,2) == IntervalMatrix([[[23,27],[34,40]],[[31,35],[46,52]]])) + + # IntervalVector operator*(const IM& x1, const IntervalVector& x2) + self.assertTrue(IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]])@IntervalVector([[5,6],[7,8]]) == IntervalVector([[19,36],[43,64]])) + self.assertTrue(IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2)@IntervalVector([[5,6],[7,8]]) == IntervalVector([[19,36],[43,64]])) + + # IntervalMatrix operator*(const IM& x1, const IM_& x2) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]])@IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2)@IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]])@IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) + self.assertTrue(IntervalMatrix([[[5,6],[6,7]],[[7,8],[8,9]]]).block(0,0,2,2)@IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]]).block(0,0,2,2) == IntervalMatrix([[[23,40],[34,53]],[[31,52],[46,69]]])) + + # The @ operator is only defined for matrix products, not for scalar ones + with self.assertRaises(TypeError): + Matrix([[1,2],[3,4]])@2. + with self.assertRaises(TypeError): + IntervalMatrix([[[1,2],[2,3]],[[3,4],[4,5]]])@Interval(-1,1) + with self.assertRaises(TypeError): + 2.@Vector([1,2]) + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/tests/core/tools/codac2_tests_transformations.py b/tests/core/tools/codac2_tests_transformations.py index 6344e1625..2851c18dc 100644 --- a/tests/core/tools/codac2_tests_transformations.py +++ b/tests/core/tools/codac2_tests_transformations.py @@ -47,6 +47,8 @@ def test_affine_transformation(self): dst_estim = SampledTraj_Vector() for ti,src_i in src: dst_estim.set(tr*src_i, ti) + if not FOR_MATLAB: # the @ operator is not available in Matlab + self.assertTrue(tr@src_i == tr*src_i) scale = tr.linear().col(0).norm() rotation_matrix = tr.linear() / scale diff --git a/tests/core/trajectory/codac2_tests_SampledTraj.py b/tests/core/trajectory/codac2_tests_SampledTraj.py index da0072f37..8090a86e1 100644 --- a/tests/core/trajectory/codac2_tests_SampledTraj.py +++ b/tests/core/trajectory/codac2_tests_SampledTraj.py @@ -131,5 +131,26 @@ def test_SampledTraj(self): for i in np.arange(0, 10, 1e-1): self.assertTrue(Approx(p(i),1e-2) == x(i)) + # SampledTraj, matrix products + + A = SampledTraj({ 0.:Matrix([[1,2],[3,4]]), 1.:Matrix([[0,1],[1,0]]) }) + B = SampledTraj({ 0.:Matrix([[0,1],[1,0]]), 1.:Matrix([[1,2],[3,4]]) }) + x = SampledTraj({ 0.:Vector([5,6]), 1.:Vector([1,2]) }) + + self.assertTrue((A*x)(0.) == Vector([17,39])) + self.assertTrue((A*x)(1.) == Vector([2,1])) + self.assertTrue((A*B)(0.) == Matrix([[2,1],[4,3]])) + self.assertTrue((A*B)(1.) == Matrix([[3,4],[1,2]])) + self.assertTrue((Matrix([[0,1],[1,0]])*A)(0.) == Matrix([[3,4],[1,2]])) + + if not FOR_MATLAB: # the @ operator is not available in Matlab + self.assertTrue((A@x)(0.) == Vector([17,39])) + self.assertTrue((A@x)(1.) == Vector([2,1])) + self.assertTrue((A@B)(0.) == Matrix([[2,1],[4,3]])) + self.assertTrue((A@B)(1.) == Matrix([[3,4],[1,2]])) + self.assertTrue((Matrix([[0,1],[1,0]])@A)(0.) == Matrix([[3,4],[1,2]])) + with self.assertRaises(TypeError): + x@x + if __name__ == '__main__': unittest.main() \ No newline at end of file