From f61e8d1f7ebdde72605a0e44e3a3e25ec1f02f8e Mon Sep 17 00:00:00 2001 From: godardma Date: Mon, 27 Apr 2026 17:29:41 +0200 Subject: [PATCH 1/2] [zonotope] added operator+ and renamed center c --- doc/manual/development/changelog.rst | 8 +++++ doc/manual/manual/geometry/zonotope.png | Bin 12274 -> 65968 bytes doc/manual/manual/geometry/zonotope.rst | 16 +++++----- .../domains/zonotope/codac2_py_Zonotope.cpp | 8 +++-- .../zonotope/codac2_Parallelepiped.cpp | 20 ++++++------ .../domains/zonotope/codac2_Parallelepiped.h | 8 ++--- src/core/domains/zonotope/codac2_Zonotope.cpp | 27 +++++++++++----- src/core/domains/zonotope/codac2_Zonotope.h | 21 ++++++++---- .../analytic/codac2_AnalyticFunction.h | 6 ++-- src/graphics/figures/codac2_Figure2D.cpp | 12 +++---- src/graphics/figures/codac2_Figure2D.h | 10 +++--- src/graphics/figures/codac2_Figure3D.cpp | 24 +++++++------- src/graphics/figures/codac2_Figure3D.h | 2 +- tests/CMakeLists.txt | 3 +- .../zonotope/codac2_tests_Parallelepiped.cpp | 2 +- .../zonotope/codac2_tests_Parallelepiped.py | 2 +- .../codac2_tests_Parallelepiped_eval.cpp | 10 +++--- .../codac2_tests_Parallelepiped_eval.py | 10 +++--- .../zonotope/codac2_tests_Zonotope.cpp | 28 ++++++++++++++++ .../domains/zonotope/codac2_tests_Zonotope.py | 30 ++++++++++++++++++ tests/core/peibos/codac2_tests_peibos.cpp | 12 +++---- tests/core/peibos/codac2_tests_peibos.py | 12 +++---- 22 files changed, 181 insertions(+), 90 deletions(-) create mode 100644 tests/core/domains/zonotope/codac2_tests_Zonotope.cpp create mode 100644 tests/core/domains/zonotope/codac2_tests_Zonotope.py diff --git a/doc/manual/development/changelog.rst b/doc/manual/development/changelog.rst index dc12a2076..1002ecf90 100644 --- a/doc/manual/development/changelog.rst +++ b/doc/manual/development/changelog.rst @@ -3,6 +3,14 @@ Changelog ========= +Upcoming version +**************** + +Pull Request #379 from godardma (27/04) +--------------------------------------- + +``Zonotope`` and ``Parallelepiped`` center is now ``c`` instead of ``z`` to avoid ambiguity. + Version 2.0.2 ************* diff --git a/doc/manual/manual/geometry/zonotope.png b/doc/manual/manual/geometry/zonotope.png index fb74b67182c2107185837d61145d7d2baa13049d..cc0584583f372a1e46d73b5f1d720087671cb7e9 100644 GIT binary patch literal 65968 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®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 literal 12274 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 L_v) +void generate_vertices(Index i, Index n, const Vector& c, const Matrix& A, std::vector& L_v) { if (i == n) { - L_v.push_back(z); + L_v.push_back(c); } else if (i Parallelepiped::vertices() const { std::vector L_v; - generate_vertices(0, z.size(),z,A,L_v); + generate_vertices(0, c.size(),c,A,L_v); return L_v; } @@ -46,9 +46,9 @@ BoolInterval Parallelepiped::contains(const Vector& v) const BoolInterval Parallelepiped::is_superset(const IntervalVector& x) const { assert_release(A.rows() == A.cols() && "Matrix A must be square to check containment."); - assert_release(x.size() == z.size() && "Point dimension must match parallelepiped dimension."); + assert_release(x.size() == c.size() && "Point dimension must match parallelepiped dimension."); - IntervalVector B = inverse_enclosure(A)*(x - z); + IntervalVector B = inverse_enclosure(A)*(x - c); IntervalVector IV = IntervalVector::constant(A.cols(),{-1,1}); if (!(B.intersects(IV))) diff --git a/src/core/domains/zonotope/codac2_Parallelepiped.h b/src/core/domains/zonotope/codac2_Parallelepiped.h index 19b6d9338..20e17bff1 100644 --- a/src/core/domains/zonotope/codac2_Parallelepiped.h +++ b/src/core/domains/zonotope/codac2_Parallelepiped.h @@ -21,9 +21,9 @@ namespace codac2 /** * \class Parallelepiped * - * \brief Class representing a parallelepiped \f$\mathbf{z} + \mathbf{A}\cdot[-1,1]^m\f$ + * \brief Class representing a parallelepiped \f$\mathbf{c} + \mathbf{A}\cdot[-1,1]^m\f$ * - * This class represents a parallelepiped in n-dimensional space, defined by a center point \f$\mathbf{z}\f$ and a shape matrix \f$\mathbf{A}\f$. + * This class represents a parallelepiped in n-dimensional space, defined by a center point \f$\mathbf{c}\f$ and a shape matrix \f$\mathbf{A}\f$. * * A parallelepiped is a special case of a zonotope where the shape matrix \f$\mathbf{A}\f$ has \f$m\f$ columns with \f$m \leqslant n\f$. */ @@ -34,10 +34,10 @@ namespace codac2 /** * \brief Constructs a n-parallelepiped object with a given center and shape matrix * - * \param z Center of the parallelepiped (n-dimensional vector) + * \param c Center of the parallelepiped (n-dimensional vector) * \param A Shape matrix of the parallelepiped (\f$n\times m\f$ matrix with \f$m \leqslant n\f$) */ - Parallelepiped(const Vector& z, const Matrix& A); + Parallelepiped(const Vector& c, const Matrix& A); /** * \brief Computes the vertices of the parallelepiped diff --git a/src/core/domains/zonotope/codac2_Zonotope.cpp b/src/core/domains/zonotope/codac2_Zonotope.cpp index 9091e7f4d..8eb9dabe8 100644 --- a/src/core/domains/zonotope/codac2_Zonotope.cpp +++ b/src/core/domains/zonotope/codac2_Zonotope.cpp @@ -11,31 +11,42 @@ using namespace codac2; -Zonotope::Zonotope(const Vector& z_, const Matrix& A_) - : z(z_), A(A_) +Zonotope::Zonotope(const Vector& c_, const Matrix& A_) + : c(c_), A(A_) { - assert_release(z.size() == A.rows()); + assert_release(c.size() == A.rows()); } IntervalVector Zonotope::box() const { - return z + (A.template cast())*IntervalVector::constant(A.cols(),{-1,1}); + return c + (A.template cast())*IntervalVector::constant(A.cols(),{-1,1}); } Zonotope Zonotope::proj(const std::vector& indices) const { assert_release(*std::min_element(indices.begin(), indices.end()) >= 0 && "indices out of range"); - assert_release(*std::max_element(indices.begin(), indices.end()) <= z.size() && "indices out of range"); + assert_release(*std::max_element(indices.begin(), indices.end()) <= c.size() && "indices out of range"); Matrix A_cropped (indices.size(), A.cols()); - Vector z_cropped (indices.size()); + Vector c_cropped (indices.size()); for (size_t i = 0; i < indices.size(); ++i) { A_cropped.row(i) = A.row(indices[i]); - z_cropped[i] = z[indices[i]]; + c_cropped[i] = c[indices[i]]; } - return Zonotope(z_cropped, A_cropped); + return Zonotope(c_cropped, A_cropped); } +Zonotope Zonotope::operator+(const Zonotope& zonotope) +{ + assert_release(c.size() == zonotope.c.size() && "Zonotopes must have the same dimension"); + + Vector c_sum = c + zonotope.c; + Matrix A_sum (A.rows(), A.cols() + zonotope.A.cols()); + + A_sum << A, zonotope.A; + + return Zonotope(c_sum, A_sum); +} diff --git a/src/core/domains/zonotope/codac2_Zonotope.h b/src/core/domains/zonotope/codac2_Zonotope.h index 863a2681b..ad1255b61 100644 --- a/src/core/domains/zonotope/codac2_Zonotope.h +++ b/src/core/domains/zonotope/codac2_Zonotope.h @@ -18,11 +18,11 @@ namespace codac2 { /** * \class Zonotope - * \brief Class representing a zonotope \f$\mathbf{z} + \mathbf{A}\cdot[-1,1]^m\f$ + * \brief Class representing a zonotope \f$\mathbf{c} + \mathbf{A}\cdot[-1,1]^m\f$ * - * This class represents a zonotope in n-dimensional space, defined by a center point \f$\mathbf{z}\f$ and a shape matrix \f$\mathbf{A}\f$. + * This class represents a zonotope in n-dimensional space, defined by a center point \f$\mathbf{c}\f$ and a shape matrix \f$\mathbf{A}\f$. * - * The vector \f$\mathbf{z}\f$ and each column of the matrix \f$\mathbf{A}\f$ must have the same dimension \f$n\f$, but the matrix \f$\mathbf{A}\f$ can have any number of columns \f$m\f$. + * The vector \f$\mathbf{c}\f$ and each column of the matrix \f$\mathbf{A}\f$ must have the same dimension \f$n\f$, but the matrix \f$\mathbf{A}\f$ can have any number of columns \f$m\f$. */ class Zonotope { @@ -31,10 +31,10 @@ namespace codac2 /** * \brief Constructs a n-zonotope object with a given center and shape matrix * - * \param z Center of the zonotope (n-dimensional vector) + * \param c Center of the zonotope (n-dimensional vector) * \param A Shape matrix of the zonotope (\f$n\times m\f$ matrix) */ - Zonotope(const Vector& z, const Matrix& A); + Zonotope(const Vector& c, const Matrix& A); /** * \brief Computes the axis-aligned bounding box of the zonotope @@ -52,10 +52,19 @@ namespace codac2 */ Zonotope proj(const std::vector& indices) const; + /** + * \brief Computes the Minkowski sum of this Zonotope with another Zonotope + * + * \param zonotope The other Zonotope to add to this one + * + * \return A new Zonotope object representing the Minkowski sum of the two Zonotopes + */ + Zonotope operator+(const Zonotope& zonotope); + /** * \brief Center of the zonotope */ - Vector z; + Vector c; /** * \brief Shape matrix of the zonotope diff --git a/src/core/functions/analytic/codac2_AnalyticFunction.h b/src/core/functions/analytic/codac2_AnalyticFunction.h index 337a64d03..0fe6fd645 100644 --- a/src/core/functions/analytic/codac2_AnalyticFunction.h +++ b/src/core/functions/analytic/codac2_AnalyticFunction.h @@ -220,17 +220,17 @@ namespace codac2 "Parallelepiped evaluation requires at least one input."); IntervalVector Y = this->eval(((typename Wrapper::Domain)(x)).mid()...); - Vector z = Y.mid(); + Vector c = Y.mid(); Matrix A = this->diff(((typename Wrapper::Domain)(x)).mid()...).mid(); // Maximum error computation - double rho = error_peibos(Y, z, this->diff(x...), A, cart_prod(x...)); + double rho = error_peibos(Y, c, this->diff(x...), A, cart_prod(x...)); // Inflation of the parallelepiped Matrix A_inf = inflate_flat_parallelepiped(A, (cart_prod(x...).template cast()).rad(), rho); - return Parallelepiped(z, A_inf); + return Parallelepiped(c, A_inf); } Index output_size() const diff --git a/src/graphics/figures/codac2_Figure2D.cpp b/src/graphics/figures/codac2_Figure2D.cpp index 53045ebc1..33cbb962b 100644 --- a/src/graphics/figures/codac2_Figure2D.cpp +++ b/src/graphics/figures/codac2_Figure2D.cpp @@ -255,7 +255,7 @@ void Figure2D::draw_zonotope(const Zonotope& z, const StyleProperties& style) } } vector vertices; - Vector point=z.z; + Vector point=z.c; // Start from v[1] maximum (and v[0] min for horizontal side) for (const auto& a : sides) { point+=a.second; @@ -277,17 +277,17 @@ void Figure2D::draw_zonotope(const Zonotope& z, const StyleProperties& style) void Figure2D::draw_parallelepiped(const Parallelepiped& p, const StyleProperties& style) { - assert_release(p.A.is_squared() && p.A.rows() == p.z.size()); - assert_release(p.z.size() == 2); + assert_release(p.A.is_squared() && p.A.rows() == p.c.size()); + assert_release(p.c.size() == 2); auto a1 = p.A.col(0), a2 = p.A.col(1); if (a1.isZero() || a2.isZero()) - draw_polyline({p.z-a1-a2,p.z+a1+a2}, style); + draw_polyline({p.c-a1-a2,p.c+a1+a2}, style); else draw_polygon({ - p.z+a1+a2, p.z-a1+a2, - p.z-a1-a2, p.z+a1-a2 + p.c+a1+a2, p.c-a1+a2, + p.c-a1-a2, p.c+a1-a2 }, style); } diff --git a/src/graphics/figures/codac2_Figure2D.h b/src/graphics/figures/codac2_Figure2D.h index 9a950ea18..3b920c734 100644 --- a/src/graphics/figures/codac2_Figure2D.h +++ b/src/graphics/figures/codac2_Figure2D.h @@ -310,7 +310,7 @@ namespace codac2 void draw_parallelepiped(const Parallelepiped& p, const StyleProperties& style = StyleProperties()); /** - * \brief Draws a zonotope z+sum_i [-1,1] A_i on the figure + * \brief Draws a zonotope c+sum_i [-1,1] A_i on the figure * * \param z Zonotope to draw (center and shape matrix) * \param style Style of the zonotope (edge color and fill color) @@ -856,15 +856,15 @@ namespace codac2 } /** - * \brief Draws a zonotope z+sum_i [-1,1] A_i on the figure + * \brief Draws a zonotope c+sum_i [-1,1] A_i on the figure * - * \param z Zonotope to draw (center and shape matrix) + * \param c Zonotope to draw (center and shape matrix) * \param style Style of the zonotope (edge color and fill color) */ - static void draw_zonotope(const Zonotope& z, const StyleProperties& style = StyleProperties()) + static void draw_zonotope(const Zonotope& c, const StyleProperties& style = StyleProperties()) { auto_init(); - selected_fig()->draw_zonotope(z,style); + selected_fig()->draw_zonotope(c,style); } /** diff --git a/src/graphics/figures/codac2_Figure3D.cpp b/src/graphics/figures/codac2_Figure3D.cpp index 86d7fcdc0..52eb04d54 100644 --- a/src/graphics/figures/codac2_Figure3D.cpp +++ b/src/graphics/figures/codac2_Figure3D.cpp @@ -105,19 +105,19 @@ void Figure3D::draw_parallelogram(const Vector &c, const Matrix &A, void Figure3D::draw_parallelepiped(const Parallelepiped& p, const StyleProperties& style) { - assert_release(p.z.size() == 3); + assert_release(p.c.size() == 3); assert_release(p.A.rows() == 3 && p.A.cols() == 3); this->set_style_internal(style); - size_t ip0 = this->move_write_v(p.z,p.A,Vector({-1,-1,-1})); - size_t ip1 = this->move_write_v(p.z,p.A,Vector({-1,-1,1})); - size_t ip2 = this->move_write_v(p.z,p.A,Vector({-1,1,-1})); - size_t ip3 = this->move_write_v(p.z,p.A,Vector({-1,1,1})); - size_t ip4 = this->move_write_v(p.z,p.A,Vector({1,-1,-1})); - size_t ip5 = this->move_write_v(p.z,p.A,Vector({1,-1,1})); - size_t ip6 = this->move_write_v(p.z,p.A,Vector({1,1,-1})); - size_t ip7 = this->move_write_v(p.z,p.A,Vector({1,1,1})); + size_t ip0 = this->move_write_v(p.c,p.A,Vector({-1,-1,-1})); + size_t ip1 = this->move_write_v(p.c,p.A,Vector({-1,-1,1})); + size_t ip2 = this->move_write_v(p.c,p.A,Vector({-1,1,-1})); + size_t ip3 = this->move_write_v(p.c,p.A,Vector({-1,1,1})); + size_t ip4 = this->move_write_v(p.c,p.A,Vector({1,-1,-1})); + size_t ip5 = this->move_write_v(p.c,p.A,Vector({1,-1,1})); + size_t ip6 = this->move_write_v(p.c,p.A,Vector({1,1,-1})); + size_t ip7 = this->move_write_v(p.c,p.A,Vector({1,1,1})); _file << "f " << ip0 << " " << ip1 << " " << ip3 << " " << ip2 << "\n"; _file << "f " << ip4 << " " << ip5 << " " << ip7 << " " << ip6 << "\n"; @@ -137,7 +137,7 @@ void Figure3D::draw_box(const IntervalVector& x, const StyleProperties& style) } void Figure3D::draw_zonotope(const Zonotope& z, const StyleProperties& style) { - assert_release(z.z.size() == 3); + assert_release(z.c.size() == 3); Matrix id = Matrix::Identity(3,3); this->set_style_internal(style); lock_style=true; @@ -186,8 +186,8 @@ void Figure3D::draw_zonotope(const Zonotope& z, const StyleProperties& style) { R1 -= Ak; } } - this->draw_parallelogram(z.z,id,R1+R2,Ai,Aj,style); - this->draw_parallelogram(z.z,id,-R1+R2,Ai,Aj,style); + this->draw_parallelogram(z.c,id,R1+R2,Ai,Aj,style); + this->draw_parallelogram(z.c,id,-R1+R2,Ai,Aj,style); } } lock_style=false; diff --git a/src/graphics/figures/codac2_Figure3D.h b/src/graphics/figures/codac2_Figure3D.h index 3887bdee1..15ea876c3 100644 --- a/src/graphics/figures/codac2_Figure3D.h +++ b/src/graphics/figures/codac2_Figure3D.h @@ -116,7 +116,7 @@ namespace codac2 void draw_parallelepiped(const Parallelepiped& p, const StyleProperties& style = { Color::dark_gray(0.5) }); /** - * \brief Draws a zonotope z+sum_i [-1,1] A_i on the figure + * \brief Draws a zonotope c+sum_i [-1,1] A_i on the figure * * \param z Zonotope to draw (center and shape matrix) * \param style Style of the zonotope (edge color) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c38934659..4ea53368d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,8 +54,9 @@ list(APPEND SRC_TESTS # listing files without extension core/domains/interval/codac2_tests_IntervalMatrix core/domains/interval/codac2_tests_IntervalVector ../doc/manual/manual/intervals/src - core/domains/zonotope/codac2_tests_Parallelepiped core/domains/zonotope/codac2_tests_Parallelepiped_eval + core/domains/zonotope/codac2_tests_Parallelepiped + core/domains/zonotope/codac2_tests_Zonotope core/domains/tube/codac2_tests_TDomain core/domains/tube/codac2_tests_Slice core/domains/tube/codac2_tests_Slice_polygon diff --git a/tests/core/domains/zonotope/codac2_tests_Parallelepiped.cpp b/tests/core/domains/zonotope/codac2_tests_Parallelepiped.cpp index 2c5766d4b..2d203740b 100644 --- a/tests/core/domains/zonotope/codac2_tests_Parallelepiped.cpp +++ b/tests/core/domains/zonotope/codac2_tests_Parallelepiped.cpp @@ -26,7 +26,7 @@ TEST_CASE("Parallelepiped") CHECK((p.is_superset(IntervalVector({{0.,5.},{2.,7.},{4.,9.}})))==BoolInterval::UNKNOWN); Zonotope z = p.proj({2,1,0}); - CHECK(z.z == Vector({4,2,0})); + CHECK(z.c == Vector({4,2,0})); CHECK(z.A == Matrix({{0.,1.,1.},{0.,1.,0.},{0.5,0.,0.}})); CHECK(z.box() == IntervalVector({{2.,6.},{1.,3.},{-0.5,0.5}})); } diff --git a/tests/core/domains/zonotope/codac2_tests_Parallelepiped.py b/tests/core/domains/zonotope/codac2_tests_Parallelepiped.py index 75b4cd987..c0eaa0a17 100644 --- a/tests/core/domains/zonotope/codac2_tests_Parallelepiped.py +++ b/tests/core/domains/zonotope/codac2_tests_Parallelepiped.py @@ -27,7 +27,7 @@ def test_parallelepiped(self): self.assertTrue((p.is_superset(IntervalVector([[0.,5.],[2.,7.],[4.,9.]])))==BoolInterval.UNKNOWN) z = p.proj([2,1,0]) - self.assertTrue(z.z == Vector([4,2,0])) + self.assertTrue(z.c == Vector([4,2,0])) self.assertTrue(z.A == Matrix([[0,1,1],[0,1,0],[0.5,0,0]])) self.assertTrue(z.box() == IntervalVector([[2,6],[1,3],[-0.5,0.5]])) diff --git a/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.cpp b/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.cpp index 0ad038b38..9dd1705b2 100644 --- a/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.cpp +++ b/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.cpp @@ -28,17 +28,17 @@ TEST_CASE("Parallelepiped_eval") auto p1a = f1.parallelepiped_eval(Interval(-0.1,0.1)); auto p1b = f1.parallelepiped_eval(1.0); - CHECK(Approx(p1a.z,1e-6) == Vector({0,0})); + CHECK(Approx(p1a.c,1e-6) == Vector({0,0})); CHECK(Approx(p1a.A,1e-6) == Matrix({{0.12,0},{0,0.02}})); - CHECK(Approx(p1b.z,1e-6) == Vector({1,1})); + CHECK(Approx(p1b.c,1e-6) == Vector({1,1})); CHECK(Approx(p1b.A,1e-6) == Matrix({{0,0},{0,0}})); auto pa = f2.parallelepiped_eval(Interval(-0.1,0.1), Interval(-0.1,0.1)); auto pb = f2.parallelepiped_eval(1.0, Interval(-1,1)); - CHECK(Approx(pa.z,1e-6) == Vector({0,0,0})); + CHECK(Approx(pa.c,1e-6) == Vector({0,0,0})); CHECK(Approx(pa.A,1e-6) == Matrix({{0.14,0,0},{0,0.14,0},{0,0,0.04}})); - CHECK(Approx(pb.z,1e-6) == Vector({1,0,1})); + CHECK(Approx(pb.c,1e-6) == Vector({1,0,1})); CHECK(Approx(pb.A,1e-5) == Matrix({{0.894428,0,1.78886},{0,3,0},{1.78886,0,-0.894427}})); @@ -55,7 +55,7 @@ TEST_CASE("Parallelepiped_eval") auto p2 = f2.parallelepiped_eval(X0,Y0); auto p3 = f3.parallelepiped_eval(IntervalVector({X0,Y0})); - CHECK(Approx(p2.z,1e-6) == p3.z); + CHECK(Approx(p2.c,1e-6) == p3.c); CHECK(Approx(p2.A,1e-6) == p3.A); y0+=dx; } diff --git a/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.py b/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.py index c7dcd18ed..5cac5ffe7 100644 --- a/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.py +++ b/tests/core/domains/zonotope/codac2_tests_Parallelepiped_eval.py @@ -27,17 +27,17 @@ def test_parallelepiped_eval(self): p1a = f1.parallelepiped_eval(Interval(-0.1,0.1)) p1b = f1.parallelepiped_eval(1.0) - self.assertTrue(Approx(p1a.z,1e-6)==Vector([0.0,0.0])) + self.assertTrue(Approx(p1a.c,1e-6)==Vector([0.0,0.0])) self.assertTrue(Approx(p1a.A,1e-6)==Matrix([[0.12,0.0],[0.0,0.02]])) - self.assertTrue(Approx(p1b.z,1e-6)==Vector([1.0,1.0])) + self.assertTrue(Approx(p1b.c,1e-6)==Vector([1.0,1.0])) self.assertTrue(Approx(p1b.A,1e-6)==Matrix([[0.0,0.0],[0.0,0.0]])) pa = f2.parallelepiped_eval(Interval(-0.1,0.1), Interval(-0.1,0.1)) pb = f2.parallelepiped_eval(1.0,Interval(-1,1)) - self.assertTrue(Approx(pa.z,1e-6)==Vector([0,0,0])) + self.assertTrue(Approx(pa.c,1e-6)==Vector([0,0,0])) self.assertTrue(Approx(pa.A,1e-6)==Matrix([[0.14,0,0],[0,0.14,0],[0,0,0.04]])) - self.assertTrue(Approx(pb.z,1e-6)==Vector([1,0,1])) + self.assertTrue(Approx(pb.c,1e-6)==Vector([1,0,1])) self.assertTrue(Approx(pb.A,1e-5)==Matrix([[0.894428,0,1.78886],[0,3,0],[1.78886,0,-0.894427]])) @@ -52,7 +52,7 @@ def test_parallelepiped_eval(self): p2 = f2.parallelepiped_eval(X0,Y0) p3 = f3.parallelepiped_eval(IntervalVector([X0,Y0])) - self.assertTrue(Approx(p2.z,1e-6)==p3.z) + self.assertTrue(Approx(p2.c,1e-6)==p3.c) self.assertTrue(Approx(p2.A,1e-6)==p3.A) y0 += dx x0 += dx diff --git a/tests/core/domains/zonotope/codac2_tests_Zonotope.cpp b/tests/core/domains/zonotope/codac2_tests_Zonotope.cpp new file mode 100644 index 000000000..60da5d7ea --- /dev/null +++ b/tests/core/domains/zonotope/codac2_tests_Zonotope.cpp @@ -0,0 +1,28 @@ +/** + * Codac tests + * ---------------------------------------------------------------------------- + * \date 2025 + * \author Maël Godard + * \copyright Copyright 2024 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include + +using namespace std; +using namespace codac2; + +TEST_CASE("Zonotope") +{ + Zonotope Z1 (Vector({2,1}), Matrix({{0.2 ,0.08}, {0.04,0.18}})); + Zonotope Z2 (Vector({2,0.5}), Matrix({{-0.2}, {0.1}})); + auto Zs = Z1+Z2; + + Vector c ({4,1.5}); + Matrix A ({{0.2, 0.08, -0.2}, + {0.04, 0.18, 0.1}}); + CHECK(Zs.c == c); + CHECK(Zs.A == A); +} + diff --git a/tests/core/domains/zonotope/codac2_tests_Zonotope.py b/tests/core/domains/zonotope/codac2_tests_Zonotope.py new file mode 100644 index 000000000..46ddd969a --- /dev/null +++ b/tests/core/domains/zonotope/codac2_tests_Zonotope.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +# Codac tests +# ---------------------------------------------------------------------------- +# \date 2025 +# \author Maël Godard +# \copyright Copyright 2024 Codac Team +# \license GNU Lesser General Public License (LGPL) + +import unittest +from codac import * +import sys +import math + +class TestZonotope(unittest.TestCase): + + def test_zonotope(self): + + Z1 = Zonotope(Vector([2,1]), Matrix([[0.2 ,0.08], [0.04,0.18]])) + Z2 = Zonotope(Vector([2,0.5]), Matrix([[-0.2], [0.1]])) + Zs = Z1+Z2 + + c = Vector([4,1.5]) + A = Matrix([[0.2 ,0.08, -0.2], + [0.04, 0.18, 0.1]]) + self.assertTrue(Zs.c == c) + self.assertTrue(Zs.A == A) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/core/peibos/codac2_tests_peibos.cpp b/tests/core/peibos/codac2_tests_peibos.cpp index bda717048..755b741e7 100644 --- a/tests/core/peibos/codac2_tests_peibos.cpp +++ b/tests/core/peibos/codac2_tests_peibos.cpp @@ -69,12 +69,12 @@ TEST_CASE("Peibos") CHECK(v_par_3d.size() == 6); - CHECK(Approx(v_par_3d[0].z,1e-6) == Vector({1.,0.,0.})); - CHECK(Approx(v_par_3d[1].z,1e-6) == Vector({0.,1.,0.})); - CHECK(Approx(v_par_3d[2].z,1e-6) == Vector({-1.,0.,0.})); - CHECK(Approx(v_par_3d[3].z,1e-6) == Vector({0.,-1.,0.})); - CHECK(Approx(v_par_3d[4].z,1e-6) == Vector({0.,0.,-1.})); - CHECK(Approx(v_par_3d[5].z,1e-6) == Vector({0.,0.,1.})); + CHECK(Approx(v_par_3d[0].c,1e-6) == Vector({1.,0.,0.})); + CHECK(Approx(v_par_3d[1].c,1e-6) == Vector({0.,1.,0.})); + CHECK(Approx(v_par_3d[2].c,1e-6) == Vector({-1.,0.,0.})); + CHECK(Approx(v_par_3d[3].c,1e-6) == Vector({0.,-1.,0.})); + CHECK(Approx(v_par_3d[4].c,1e-6) == Vector({0.,0.,-1.})); + CHECK(Approx(v_par_3d[5].c,1e-6) == Vector({0.,0.,1.})); double a = 4.35066; diff --git a/tests/core/peibos/codac2_tests_peibos.py b/tests/core/peibos/codac2_tests_peibos.py index 66042d8e0..f4f12191e 100644 --- a/tests/core/peibos/codac2_tests_peibos.py +++ b/tests/core/peibos/codac2_tests_peibos.py @@ -63,12 +63,12 @@ def test_peibos(self): self.assertTrue(len(v_par_3d) == 6) - self.assertTrue(Approx(v_par_3d[0].z,1e-6) == Vector([1.,0.,0.])) - self.assertTrue(Approx(v_par_3d[1].z,1e-6) == Vector([0.,1.,0.])) - self.assertTrue(Approx(v_par_3d[2].z,1e-6) == Vector([-1,0.,0.])) - self.assertTrue(Approx(v_par_3d[3].z,1e-6) == Vector([0.,-1.,0.])) - self.assertTrue(Approx(v_par_3d[4].z,1e-6) == Vector([0.,0.,-1.])) - self.assertTrue(Approx(v_par_3d[5].z,1e-6) == Vector([0.,0.,1.])) + self.assertTrue(Approx(v_par_3d[0].c,1e-6) == Vector([1.,0.,0.])) + self.assertTrue(Approx(v_par_3d[1].c,1e-6) == Vector([0.,1.,0.])) + self.assertTrue(Approx(v_par_3d[2].c,1e-6) == Vector([-1,0.,0.])) + self.assertTrue(Approx(v_par_3d[3].c,1e-6) == Vector([0.,-1.,0.])) + self.assertTrue(Approx(v_par_3d[4].c,1e-6) == Vector([0.,0.,-1.])) + self.assertTrue(Approx(v_par_3d[5].c,1e-6) == Vector([0.,0.,1.])) a = 4.35066 From f815210ad58823d5e8474af095d1fe1dc01513bc Mon Sep 17 00:00:00 2001 From: godardma Date: Thu, 30 Apr 2026 22:46:33 +0200 Subject: [PATCH 2/2] [graphics] possibility to choose origin for axes --- python/src/graphics/figures/codac2_py_Figure3D.cpp | 4 ++-- src/graphics/figures/codac2_Figure3D.cpp | 5 +++-- src/graphics/figures/codac2_Figure3D.h | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/python/src/graphics/figures/codac2_py_Figure3D.cpp b/python/src/graphics/figures/codac2_py_Figure3D.cpp index 24dbf165f..afbc719d9 100644 --- a/python/src/graphics/figures/codac2_py_Figure3D.cpp +++ b/python/src/graphics/figures/codac2_py_Figure3D.cpp @@ -35,8 +35,8 @@ void export_Figure3D(py::module& m) CONST_STRING_REF_FIGURE3D_NAME_CONST) .def("draw_axes", &Figure3D::draw_axes, - VOID_FIGURE3D_DRAW_AXES_DOUBLE, - "size"_a=1.0) + VOID_FIGURE3D_DRAW_AXES_DOUBLE_CONST_VECTOR_REF, + "size"_a=1.0, "origin"_a=Vector::Zero(3)) // Geometric shapes .def("draw_triangle", (void(Figure3D::*)(const Vector &c, const Matrix &A, const Vector &p1, const Vector &p2, const Vector &p3, const StyleProperties &s))&Figure3D::draw_triangle, diff --git a/src/graphics/figures/codac2_Figure3D.cpp b/src/graphics/figures/codac2_Figure3D.cpp index 52eb04d54..ca0aee3ef 100644 --- a/src/graphics/figures/codac2_Figure3D.cpp +++ b/src/graphics/figures/codac2_Figure3D.cpp @@ -214,10 +214,11 @@ void Figure3D::draw_arrow(const Vector& c, const Matrix &A, } -void Figure3D::draw_axes(double size) +void Figure3D::draw_axes(double size, const Vector& origin) { + assert_release(origin.size()==3); const std::string name="axes"; - Vector z = Vector::zero(3); + Vector z = origin; // X axis Matrix AX = Matrix::Identity(3,3); draw_arrow(z,size*AX,StyleProperties(Color::red(),name)); diff --git a/src/graphics/figures/codac2_Figure3D.h b/src/graphics/figures/codac2_Figure3D.h index 15ea876c3..442293832 100644 --- a/src/graphics/figures/codac2_Figure3D.h +++ b/src/graphics/figures/codac2_Figure3D.h @@ -146,8 +146,9 @@ namespace codac2 * \brief Draws the (x,y,z) axes on the figure in red, green and blue * * \param size Size of the axes + * \param origin Origin of the axes */ - void draw_axes(double size = 1.0); + void draw_axes(double size = 1.0, const Vector& origin = Vector::Zero(3)); /** * \brief Draws a parametric surface