diff --git a/README.md b/README.md index 1b23427c9..f6e21a60f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# [Codac: constraint-programming for robotics](http://codac.io/v2) [![Build Status](https://github.com/codac-team/codac/workflows/.github/workflows/tests.yml/badge.svg)](https://github.com/codac-team/codac/actions) +# [Codac: constraint-programming for robotics](http://codac.io) [![Build Status](https://github.com/codac-team/codac/workflows/.github/workflows/tests.yml/badge.svg)](https://github.com/codac-team/codac/actions) -See the official website: http://codac.io/v2 +See the official website: http://codac.io Codac (Catalog Of Domains And Contractors) is a C++/Python/Matlab library providing tools for interval computations and constraint programming over real numbers, trajectories and sets. It has numerous applications in parameter estimation, guaranteed integration, robot localization, and provides reliable outputs. diff --git a/doc/manual/_static/css/custom.css b/doc/manual/_static/css/custom.css index 7db716235..44a8b663a 100644 --- a/doc/manual/_static/css/custom.css +++ b/doc/manual/_static/css/custom.css @@ -179,16 +179,21 @@ a .download:before { } /* Adding VIBes + IPE logos in the lateral menu */ -.sidebar-tree li.toctree-l1 > a.reference.internal:has(+ input#toctree-checkbox-8) { +.sidebar-tree li.toctree-l1 > a.reference.internal:has(+ input#toctree-checkbox-9) { background:url("../logos/logos_vibes_ipe.png") no-repeat center / contain; } -.sidebar-tree li.toctree-l1 > a.reference.internal:has(+ input#toctree-checkbox-8):hover { +.sidebar-tree li.toctree-l1 > a.reference.internal:has(+ input#toctree-checkbox-9):hover { background: url("../logos/logos_vibes_ipe.png") no-repeat center / contain, var(--color-sidebar-item-background--hover); } -.sidebar-tree li.toctree-l1 > a.reference.internal.current:has(+ input#toctree-checkbox-8) { +.sidebar-tree li.toctree-l1 > a.reference.internal.current:has(+ input#toctree-checkbox-9) { background: url("../logos/logos_vibes_ipe.png") no-repeat center / contain, var(--color-sidebar-item-background--current); +} + +figcaption .caption-text +{ + font-style: italic; } \ No newline at end of file diff --git a/doc/manual/conf.py.in b/doc/manual/conf.py.in index 31f65d821..8c92a6019 100644 --- a/doc/manual/conf.py.in +++ b/doc/manual/conf.py.in @@ -89,7 +89,7 @@ html_theme_options = { "color-toc-item-text--active": "#FFFFFF", }, - "announcement": "You are currently reading the new Codac documentation, which is currently in preparation. See the v1 website.", + #"announcement": "You are currently reading the new Codac documentation, which is currently in preparation. See the v1 website.", } # See https://www.sympy.org/sphinx-math-dollar/ diff --git a/doc/manual/index.rst b/doc/manual/index.rst index 3ad3e0a2c..8c38197fd 100644 --- a/doc/manual/index.rst +++ b/doc/manual/index.rst @@ -213,10 +213,9 @@ User manual * The class SetMembershipFunction * Extension to custom expressions -* Tubes - * What is a tube? - * Temporal domains - * The Tube classes +* :ref:`sec-domains-tubes` + * :ref:`sec-domains-tubes-tdomain` + * :ref:`sec-domains-tubes-slicedtube` * The Trajectory classes * Increasing performances using views @@ -401,6 +400,7 @@ Development manual/introduction/index.rst manual/installation/index.rst manual/intervals/index.rst + manual/tubes/index.rst manual/linear/index.rst manual/functions/index.rst manual/contractors/index.rst diff --git a/doc/manual/manual/functions/analytic/analytic_operators.rst b/doc/manual/manual/functions/analytic/analytic_operators.rst index 4262b4644..f1c8a3a19 100644 --- a/doc/manual/manual/functions/analytic/analytic_operators.rst +++ b/doc/manual/manual/functions/analytic/analytic_operators.rst @@ -321,6 +321,8 @@ Expressions involving a non-supported centered-form operation If an operator, for which the centered form is not defined, is involved in an expression, then this expression cannot be evaluated using the centered form (calculation is disabled for the entire operation). A simple natural evaluation will then be computed. +.. _sec-functions-temporal-operator: + Expressions involving a temporal operator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -330,7 +332,7 @@ Temporal operations for involving trajectories or tubes in analytic expressions .. code-tab:: py - x = # some sampled trajectory... + x = # some tube or sampled trajectory... g = x.as_function() # intermediate operation t = ScalarVar() @@ -340,7 +342,7 @@ Temporal operations for involving trajectories or tubes in analytic expressions .. code-tab:: c++ - x = // some sampled trajectory... + x = // some tube or sampled trajectory... g = x.as_function(); // intermediate operation ScalarVar t; diff --git a/doc/manual/manual/installation/cpp.rst b/doc/manual/manual/installation/cpp.rst index 1b97da510..9f0399474 100644 --- a/doc/manual/manual/installation/cpp.rst +++ b/doc/manual/manual/installation/cpp.rst @@ -190,7 +190,7 @@ Optionally, for Python binding (*e.g.* ``choco install python --version=3.10.4`` .. code-block:: bash - choco install doxygen.install --version=1.13 + choco install doxygen.install --version=1.16 choco install graphviz python -m pip install --upgrade pip pip install --upgrade wheel setuptools sphinx sphinx_rtd_theme furo sphinx-math-dollar sphinx_tabs breathe sphinx_togglebutton diff --git a/doc/manual/manual/installation/matlab.rst b/doc/manual/manual/installation/matlab.rst index 696797f78..0fe6071fe 100644 --- a/doc/manual/manual/installation/matlab.rst +++ b/doc/manual/manual/installation/matlab.rst @@ -13,8 +13,7 @@ Since 2019, MATLAB allows you to import Python packages into its environment. Co .. code-block:: bash - pip install codac4matlab --pre - # Option --pre has to be set because Codac v2 is only available in pre-release + pip install codac4matlab Once Codac is installed, you can use it in your MATLAB scripts using the following import command: diff --git a/doc/manual/manual/installation/python.rst b/doc/manual/manual/installation/python.rst index d06aee3a1..0cdcfeb2c 100644 --- a/doc/manual/manual/installation/python.rst +++ b/doc/manual/manual/installation/python.rst @@ -7,22 +7,20 @@ Python installation In case you want to use Codac only with Python, then the installation procedure is simply: -1. **Dependencies**: Ensure you have a supported version of Python (>=3.6). +1. **Dependencies**: Ensure you have a supported version of Python (>=3.8). 2. **Install via pip**: Use the following command to install Codac: .. code-block:: bash - pip install codac --pre - # Option --pre has to be set because Codac v2 is only available in pre-release + pip install codac -You can also update and test your Codac Python package. +You can also update and test your Codac Python package: * **Update via pip**: Use the following command to update your already installed Codac package: .. code-block:: bash - pip install codac --upgrade --pre - # Option --pre has to be set because Codac v2 is only available in pre-release + pip install codac --upgrade * **Testing the installation**: In order to verify that your installation is working properly in Python, you can run: @@ -42,7 +40,7 @@ Depending on your configuration, you may encounter difficulties when installing .. code-block:: bash - pip3 install --break-system-packages codac --pre + pip3 install --break-system-packages codac .. admonition:: macOS Big Sur and later (x86_64) diff --git a/doc/manual/manual/tools/index.rst b/doc/manual/manual/tools/index.rst index 88bc066a1..c774c6c8e 100644 --- a/doc/manual/manual/tools/index.rst +++ b/doc/manual/manual/tools/index.rst @@ -8,4 +8,5 @@ Tools serialization.rst registration.rst - octasym.rst \ No newline at end of file + octasym.rst + sampled_traj_npz.rst \ No newline at end of file diff --git a/doc/manual/manual/tools/sampled_traj_npz.rst b/doc/manual/manual/tools/sampled_traj_npz.rst new file mode 100644 index 000000000..822ce9b6e --- /dev/null +++ b/doc/manual/manual/tools/sampled_traj_npz.rst @@ -0,0 +1,66 @@ +.. _sec-trajectories-sampledtraj: + +Sampled trajectories +==================== + +In Python, the ``SampledTraj`` name is a convenience wrapper that mimics the +C++ template deduction style. Depending on the provided samples, it builds and +returns one of the following objects: + +- ``SampledTraj_Scalar`` +- ``SampledTraj_Vector`` +- ``SampledTraj_Matrix`` + +This keeps the user-facing syntax short and consistent with ``AnalyticTraj``. + + +Loading a sampled trajectory from a ``.npz`` file +------------------------------------------------- + +A sampled trajectory cannot be constructed directly from a file path. +The recommended workflow is: + +1. load the ``.npz`` file with ``numpy.load``; +2. extract the arrays containing the sampling times and sampled values; +3. call ``SampledTraj(t, x)``. + +The wrapper then deduces the appropriate trajectory type from ``x``. + +.. tabs:: + + .. group-tab:: Python + + .. code-block:: python + + import numpy as np + from codac import * + + data = np.load("traj_vec.npz") + traj = SampledTraj(data["t"], data["x"]) + + print(type(traj)) + +In the above example, if ``x`` is a 2D array of shape ``(N,n)``, then +``traj`` is a ``SampledTraj_Vector``. + +Another example: + +.. tabs:: + + .. group-tab:: Python + + .. code-block:: python + + import numpy as np + from codac import * + + t = np.array([0.0, 0.5, 1.0, 1.5]) + x = np.array([ + [0.0, 1.0], + [0.5, 1.5], + [1.0, 2.0], + [1.5, 2.5], + ]) + + traj = SampledTraj(t, x) + print(traj) # outputs: SampledTraj. [0, 1.5]↦[[0, 1.5][1, 2.5]], 4 pts \ No newline at end of file diff --git a/doc/manual/manual/tubes/index.rst b/doc/manual/manual/tubes/index.rst new file mode 100644 index 000000000..e1baee6dc --- /dev/null +++ b/doc/manual/manual/tubes/index.rst @@ -0,0 +1,66 @@ +.. _sec-domains-tubes: + +Tubes +===== + + Main author: `Simon Rohou `_ + +Introduction +------------ + +A tube is a set of trajectories. More precisely, if :math:`\mathcal{F}(\mathbb{R}\to\mathbb{R}^n)` denotes the set of trajectories with values in :math:`\mathbb{R}^n`, a tube :math:`\mathbb{X}(\cdot)` is a set-valued map + +.. math:: + + \mathbb{X}(\cdot): \mathbb{R} \to \mathcal{P}(\mathbb{R}^n) + +such that + +.. math:: + + \mathbf{x}(\cdot)\in\mathbb{X}(\cdot) + \;\Longleftrightarrow\; + \forall t,\ \mathbf{x}(t)\in\mathbb{X}(t). + +In other words, a tube encloses trajectories pointwise in time: for every time :math:`t`, the value :math:`\mathbf{x}(t)` must belong to the set :math:`\mathbb{X}(t)`. This definition is intentionally general. It does not encode additional temporal information such as continuity, monotonicity, or derivative constraints. Such properties may be handled separately, for instance by contractors or by combining several tubes. + +This point of view naturally extends interval and box representations. In the most common cases, :math:`\mathbb{X}(t)` is an interval, a box, or a matrix of intervals, but in principle it may be any codomain type for which suitable set operations are available. + +In Codac C++, this generality is reflected by the template ``SlicedTube``. In principle, ``T`` can be any codomain type compatible with the sliced-tube algorithms. +The library naturally provides and primarily uses: + +* ``SlicedTube``, +* ``SlicedTube``, +* ``SlicedTube``. + +These are the most common tube types in practice. In Python and Matlab, only these standard sliced-tube classes are currently exposed through the bindings. +In C++, instantiations such as ``SlicedTube`` are allowed, provided the required tube operations are available for that domain. + +This chapter documents the *sliced tube* implementation currently available in Codac. In this implementation, a tube is represented over an explicit temporal partition stored in a :class:`~codac.TDomain`. Each temporal element is a :class:`~codac.TSlice`, and each application-level value over one temporal element is represented by a :class:`~codac.Slice`. The main user-facing object is therefore a :class:`~codac.SlicedTube`. + +.. figure:: ./tube_slices.png + :width: 70% + + An example of sliced tube :math:`[x](\cdot)`, as implemented in Codac. The tube is made as a list of interval or box slices. In practice, the sampling :math:`\delta` is not necessarily constant. + +Compared with the previous Codac v1 implementation, these tubes are not only functions of time: they also have shared data structures built on a common temporal partition. Refining the time partition of one tube may therefore update the internal slice structure of all tubes attached to the same :class:`~codac.TDomain`. This design is central to the current implementation. It simplifies the user experience: a temporal modification applied to one tube is implicitly propagated to the others, so multi-tube operations such as arithmetic combinations or contractions can still be performed easily on a consistent shared time basis. + +Conceptually, the data model is: + +.. code-block:: text + + TDomain + ├── TSlice #0 ----> Slice for tube x + │ └-> Slice for tube y + ├── TSlice #1 ----> Slice for tube x + │ └-> Slice for tube y + └── ... + + +This chapter is organized as follows: + +.. toctree:: + :maxdepth: 1 + + tdomain + slicedtube \ No newline at end of file diff --git a/doc/manual/manual/tubes/interval_trajs.png b/doc/manual/manual/tubes/interval_trajs.png new file mode 100644 index 000000000..8533ce3c9 Binary files /dev/null and b/doc/manual/manual/tubes/interval_trajs.png differ diff --git a/doc/manual/manual/tubes/inversion_example.png b/doc/manual/manual/tubes/inversion_example.png new file mode 100644 index 000000000..f99f47a74 Binary files /dev/null and b/doc/manual/manual/tubes/inversion_example.png differ diff --git a/doc/manual/manual/tubes/slicedtube.rst b/doc/manual/manual/tubes/slicedtube.rst new file mode 100644 index 000000000..87202c2ae --- /dev/null +++ b/doc/manual/manual/tubes/slicedtube.rst @@ -0,0 +1,354 @@ +.. _sec-domains-tubes-slicedtube: + +The SlicedTube class +==================== + + Main author: `Simon Rohou `_ + +A :class:`~codac.SlicedTube` is a tube defined over a shared :class:`~codac.TDomain`. +For each temporal slice stored in the temporal partition, the tube owns one :class:`~codac.Slice` object storing its codomain over that temporal support. The codomain type is typically :class:`~codac.Interval` or :class:`~codac.IntervalVector`, or any domain type defined by the user. The same temporal domain can be shared by many tubes. + + +Creating sliced tubes +--------------------- + +In Codac C++, :class:`~codac.SlicedTube` is a generic class template ``SlicedTube``. The codomain type ``T`` is not restricted to intervals or boxes: in principle, any domain type compatible with the sliced-tube API can be used. + +In practice, the most common sliced-tube types are: + +* ``SlicedTube``, +* ``SlicedTube``, +* ``SlicedTube``. + +These standard tube types are also the ones exposed in the Python and Matlab bindings. + +In C++, template argument deduction is available for the constructors based on the arguments. In the examples below, comments make the deduced type explicit when useful. + +A sliced tube can be created from: + +* a constant codomain, +* an analytic function of one scalar variable, +* a sampled or an analytic trajectory, +* or another sliced tube (copy constructor). + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-1-beg] + :end-before: [slicedtube-class-1-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-1-beg] + :end-before: [slicedtube-class-1-end] + :dedent: 4 + + +.. figure:: interval_trajs.png + :width: 70% + + Created tubes :math:`[x_f](\cdot)=[\sin(\cdot),\sin(\cdot)]` and :math:`[x_u](\cdot)=[\cos(\cdot),\cos(\cdot)+\frac{\cdot}{10}]`, made of 100 slices. + + +Basic properties +---------------- + +A sliced tube exposes: + +* ``size()`` for the codomain dimension, +* ``shape()`` for matrix-like codomain shapes, +* ``tdomain()`` and ``t0_tf()`` from the base classes, +* ``nb_slices()`` for the number of temporal elements, +* ``codomain()`` for the global codomain hull, +* ``volume()`` for the sum of non-gate slice volumes, +* ``is_empty()`` and ``is_unbounded()``. + +Accessing slices and iterating +------------------------------ + +You can access the first and last slices directly: + +* ``first_slice()`` +* ``last_slice()`` + +and you can retrieve a slice from a temporal-domain iterator or from a ``TSlice`` pointer. +The class also defines custom iterators so that iterating on a ``SlicedTube`` yields application-level :class:`~codac.Slice` objects rather than raw ``TSlice`` objects. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-2-beg] + :end-before: [slicedtube-class-2-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-2-beg] + :end-before: [slicedtube-class-2-end] + :dedent: 4 + + +Setting values and refining the partition +----------------------------------------- + +A sliced tube can be updated globally or locally: + +* ``set(codomain)`` sets all slices to the same codomain, +* ``set(codomain, t)`` sets the value at one time instant, +* ``set(codomain, [ta,tb])`` sets all temporal elements intersecting an interval, +* ``set_ith_slice(codomain, i)`` sets one stored slice by index. + +The key point is that local assignments may refine the underlying :class:`~codac.TDomain`. +For example, setting a value at one scalar time creates an explicit gate if necessary. Similarly, setting a value over an interval may create new temporal boundaries at the interval endpoints. + +.. note:: + + Because the temporal domain is shared, such refinements are structural operations. If several tubes share the same ``TDomain``, all of them will observe the new partition. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-4-beg] + :end-before: [tdomain-class-4-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-4-beg] + :end-before: [tdomain-class-4-end] + :dedent: 4 + + +Evaluation +---------- + +A sliced tube :math:`[x](\cdot)` can be evaluated over a temporal interval :math:`[t]` with ``x(t)``. The implementation walks through all relevant temporal slices, evaluates each local slice, and unions the results. If the query interval is not included in the temporal domain, the result is the unbounded value of the codomain type (for interval tubes: :math:`[-\infty,\infty]`). + +When a derivative tube ``v`` is available, the overload ``x(t,v)`` uses per-slice derivative-aware evaluation and unions the resulting enclosures. This is available for ``Interval`` and ``IntervalVector`` codomain types. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-4-beg] + :end-before: [slicedtube-class-4-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-4-beg] + :end-before: [slicedtube-class-4-end] + :dedent: 4 + + +Inversion +--------- + +The inversion of a sliced tube :math:`[x](\cdot)`, denoted :math:`[x]^{-1}([y])`, is defined by + +.. math:: + + [x]^{-1}([y]) + = + \left\{\, t \mid [x](t)\cap [y]\neq\varnothing \,\right\} + = + \bigcup_{y\in [y]} \left\{\, t \mid y\in [x](t) \,\right\}. + +It is illustrated by the figure below. Intuitively, the inversion returns the set of time values whose image under :math:`[x](\cdot)` intersects the target set :math:`[y]`. + +Depending on the overload, the ``invert()`` methods + +* ``invert(y, t=[...])`` +* ``invert(y, v_t, t=[...])`` +* ``invert(y, v, t=[...])`` +* ``invert(y, v_t, v, t=[...])`` + +return either: + +* one interval enclosing the union of all preimages, +* or the different connected components of the inversion in a vector of :class:`~codac.Interval` objects. + +When a derivative tube ``v`` is provided, Codac uses derivative information slice by slice in order to compute sharper inverse images. + +The following example computes the different connected components of the inversion :math:`[x]^{-1}([0,0.2])` over a temporal subdomain, and then draws their projections in red. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-5-beg] + :end-before: [slicedtube-class-5-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-5-beg] + :end-before: [slicedtube-class-5-end] + :dedent: 4 + + +.. figure:: inversion_example.png + :width: 70% + + Example of tube inversion for a given tube :math:`[x](\cdot)`. The red boxes correspond to the connected components of :math:`[x]^{-1}([0,0.2])`. + +.. doxygengroup:: codac2_slicedtube_inversion + :project: codac + + +Tube operations +--------------- + +Standard pointwise operations are available on sliced tubes and are applied slice by slice. + +For two tubes, binary operators require both operands to share the same :class:`~codac.TDomain`. This makes tube expressions easy to combine once the tubes have been built on a common temporal support. + +The following operators are available: + +* union and intersection: ``|`` and ``&``, +* arithmetic operations: ``+``, ``-``, ``*``, ``/``, +* in-place variants: ``|=``, ``&=``, ``+=``, ``-=``, ``*=``, ``/=``. + +For scalar interval tubes, several usual nonlinear functions are also provided, including ``sqr``, ``sqrt``, ``pow``, ``exp``, ``log``, ``sin``, ``cos``, ``tan``, ``atan2``, ``abs``, ``min``, ``max``, ``sign``, ``floor`` and ``ceil``. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-6-beg] + :end-before: [slicedtube-class-6-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-6-beg] + :end-before: [slicedtube-class-6-end] + :dedent: 4 + +For vector-valued codomains, pointwise arithmetic is also available. In particular, the product of a matrix tube and a vector tube returns a vector tube. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-7-beg] + :end-before: [slicedtube-class-7-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-7-beg] + :end-before: [slicedtube-class-7-end] + :dedent: 4 + + +Integration and primitives +-------------------------- + +Reliable integral computations are available on tubes. + +.. figure:: tube_integ_inf.png + :width: 70% + + Hatched part depicts :math:`\int_{a}^{b}x^-(\tau)d\tau`, the lower bound of :math:`\int_{a}^{b}[x](\tau)d\tau`. + +The computation is reliable even for tubes defined from analytic functions, because it stands on the tube's slices that are reliable enclosures. The result is an outer approximation of the integral of the tube represented by these slices: + +.. figure:: tube_lb_integral_slices.png + :width: 70% + + Outer approximation of the lower bound of :math:`\int_{a}^{b}[x](\tau)d\tau`. + + +.. doxygengroup:: codac2_slicedtube_integrals + :project: codac + + +Inflation, extraction, and algebraic helpers +-------------------------------------------- + +A sliced tube can be inflated either by a constant radius or by a time-varying +sampled radius. Inflation is performed in place. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-8-beg] + :end-before: [slicedtube-class-8-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-8-beg] + :end-before: [slicedtube-class-8-end] + :dedent: 4 + + +For vector-valued tubes, the API also provides convenient extraction operators: + +* ``x[i]`` returns the :math:`i`-th scalar component as a + :class:`~codac.SlicedTube`, +* ``x.subvector(i,j)`` returns a subvector tube. + +The extracted tubes keep the same temporal partition as the original one. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [slicedtube-class-9-beg] + :end-before: [slicedtube-class-9-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [slicedtube-class-9-beg] + :end-before: [slicedtube-class-9-end] + :dedent: 4 + + +Involving a tube in an analytic expression +------------------------------------------ + +The method ``.as_function()`` wraps a sliced tube as an analytic operator so that it can be embedded inside analytic expressions. This is a convenient bridge between the tube API and the analytic-expression API, see :ref:`the section on temporal operators ` for its use. \ No newline at end of file diff --git a/doc/manual/manual/tubes/src.cpp b/doc/manual/manual/tubes/src.cpp new file mode 100644 index 000000000..8e3a932f3 --- /dev/null +++ b/doc/manual/manual/tubes/src.cpp @@ -0,0 +1,248 @@ +/** + * Codac tests + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Simon Rohou + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace codac2; + +TEST_CASE("TDomain class - manual") +{ + { + // [tdomain-class-1-beg] + auto td0 = create_tdomain(); // one unbounded slice [-oo,oo] + auto td1 = create_tdomain({0,1}); // one slice [0,1] + auto td2 = create_tdomain({0,1}, 0.5, false); // [0,0.5],[0.5,1] + auto td3 = create_tdomain({0,1}, 0.5, true); // [0],[0,0.5],[0.5],[0.5,1],[1] + // [tdomain-class-1-end] + } + + { + // [tdomain-class-2-beg] + auto td = create_tdomain({0,1}, 0.5, true); // returns a std::shared_ptr + + Interval dom = td->t0_tf(); // [0,1] + size_t n = td->nb_tslices(); // 5 + size_t m = td->nb_tubes(); // 0 initially + + auto it = td->tslice(0.0); // [0] + it = td->tslice(0.1); // [0,0.5] + it = td->tslice(0.5); // [0.5] + it = td->tslice(0.6); // [0.5,1] + it = td->tslice(1.0); // [1] + // [tdomain-class-2-end] + CHECK(n == 5); + CHECK(m == 0); + CHECK(dom == Interval(0,1)); + } + + { + // [tdomain-class-3-beg] + auto td = create_tdomain(); // returns a std::shared_ptr + td->sample(1., false); // [-oo,1],[1,oo] + td->sample(10., true); // [-oo,1],[1,10],[10],[10,oo] + // [tdomain-class-3-end] + } + + { + // [tdomain-class-4-beg] + auto td = create_tdomain({0,2}, 1.0, false); // false: without gates + + SlicedTube x(td, Interval(0,1)); + SlicedTube v(td, Interval(-1,1)); + + cout << x << endl; // outputs [0,2]->[0,1], 2 slices + cout << v << endl; // outputs [0,2]->[0,1], 2 slices + + size_t n = td->nb_tslices(); // 2: [0,1],[1,2] + x.set({0.5,1}, 1.3); // local update, will refine the partition at t=1.3 + size_t m = td->nb_tslices(); // now 4: [0,1],[1,1.3],[1.3],[1.3,2] + + cout << x << endl; // outputs [0,2]->[-1,1], 4 slices + cout << v << endl; // outputs [0,2]->[-1,1], 4 slices (v is also impacted by x.set(..)) + // [tdomain-class-4-end] + CHECK(n == 2); + CHECK(m == 4); + } + + { + // [tdomain-class-5-beg] + auto td = create_tdomain({0,3}, 1.0, false); + SlicedTube x(td, Interval(0,1)); + + cout << td->nb_tubes() << endl; // 1 + td->truncate({0.5,2.5}); + + cout << td->t0_tf() << endl; // [0.5,2.5] + cout << x << endl; // x now uses the truncated shared partition + // [tdomain-class-5-end] + } +} + +TEST_CASE("SlicedTube class - manual") +{ + Figure2D fig("Tube", GraphicOutput::VIBES); + fig.set_window_properties({50,50},{800,400}); + + { + // [slicedtube-class-1-beg] + auto td = create_tdomain({0,10}, 0.1, true); + + // Sliced tube from interval-type codomains + SlicedTube x(td, Interval(-1,1)); // x has type SlicedTube + SlicedTube y(td, IntervalVector(2)); // y has type SlicedTube + SlicedTube z(td, IntervalMatrix(2,2)); // z has type SlicedTube + + // Explicit template notation remains possible: + SlicedTube x2(td, Interval(-1,1)); + + // From an analytic function + ScalarVar t; + AnalyticFunction f({t}, sin(t)); + SlicedTube xf(td, f); + // xf has type SlicedTube because f outputs scalar values + + // From an analytic trajectory or sampled trajectory + AnalyticTraj at({0,10}, AnalyticFunction({t},cos(t))); + SampledTraj st = AnalyticTraj({0,10}, AnalyticFunction({t},cos(t)+t/10)).sampled(1e-2); + auto xu = SlicedTube(td,at) | SlicedTube(td,st); // union (hull) of tubes + + fig.plot_tube(xf, {Color::dark_blue(),Color::light_gray()}); + fig.plot_tube(xu, {Color::dark_blue(),Color::light_gray()}); + // [slicedtube-class-1-end] + } + + { + // [slicedtube-class-2-beg] + auto td = create_tdomain({0,3}); + SlicedTube x(td, IntervalVector(2)); + x.set({{1,5},{-oo,2}}, {0,1}); + x.set({{2,8},{-oo,3}}, {1,2}); + x.set({{6,9},{-oo,4}}, {2,3}); + + auto s0 = x.first_slice(); + auto s1 = s0->next_slice(); + + for(const auto& s : x) + std::cout << s.t0_tf() << " -> " << s.codomain() << std::endl; + // [slicedtube-class-2-end] + } + + { + // [slicedtube-class-4-beg] + auto td = create_tdomain({0,3}, 1.0, false); + SlicedTube x(td, Interval()); + + x.set({1,5}, {0,1}); + x.set({2,8}, {1,2}); + x.set({6,9}, {2,3}); + + Interval y0 = x(0.5); // [1,5] + Interval y1 = x(1.5); // [2,8] + Interval y2 = x({0,3}); // [1,9] + Interval y3 = x(-1.0); // [-oo,oo] + + // No explicit gates: boundary values come from adjacent-slice intersections + Interval y4 = x(1.0); // [2,5] + Interval y5 = x(2.0); // [6,8] + Interval y6 = x(3.0); // [6,9] + // [slicedtube-class-4-end] + } + + { + Interval T(0,10); + auto td = create_tdomain(T, 1e-1); + + ScalarVar t; + + AnalyticTraj z1(T, AnalyticFunction({t},cos(t))); + AnalyticTraj z2(T, AnalyticFunction({t},cos(t)+t/10)); + AnalyticTraj z3(T, AnalyticFunction({t},sin(t)+t/10)); + SampledTraj z4 = AnalyticTraj(T, AnalyticFunction({t},sin(t))).sampled(1e-2); + + SlicedTube x1(td, z1); + SlicedTube x2(td, z2); + SlicedTube x3(td, z3); + SlicedTube x4(td, z4); + + auto x = x1 | x2 | x3 | x4; + + // [slicedtube-class-5-beg] + std::vector v_t; + Interval y(0,0.2); + x.invert(y, v_t); + + for(const auto& t : v_t) + { + IntervalVector z = cart_prod(t,y); + DefaultFigure::draw_box(z, Color::red()); + } + // [slicedtube-class-5-end] + } + + { + // [slicedtube-class-6-beg] + auto td = create_tdomain({0,2}, 1.0, false); + + SlicedTube x(td, Interval(1,2)); + SlicedTube y(td, Interval(-1,3)); + + auto z_add = x + y; // addition of two tubes + auto z_mul = 2. * x; // multiplication by a scalar/interval + auto z_hul = x | y; // hull (union) of two tubes + auto z_int = x & y; // intersection of two tubes + + auto u = sin(x) + exp(y); + // [slicedtube-class-6-end] + } + + { + auto td = create_tdomain(); + + // [slicedtube-class-7-beg] + SlicedTube A(td, IntervalMatrix(2,2)); + SlicedTube b(td, IntervalVector(2)); + + auto y = A * b; // type: SlicedTube + // [slicedtube-class-7-end] + } + + { + auto td = create_tdomain({0,2}); + SlicedTube x(td, Interval()); + + // [slicedtube-class-8-beg] + x.inflate(0.2); // constant inflation + + SampledTraj rad({{0.0,0.11}, {1.0,0.3}, {2.0,0.2}}); + x.inflate(rad); // time-varying inflation radius + // [slicedtube-class-8-end] + } + + { + auto td = create_tdomain(); + SlicedTube x(td, IntervalVector(3)); + + // [slicedtube-class-9-beg] + // Component and subvector extraction + auto x0 = x[0]; // type: SlicedTube + auto x12 = x.subvector(1,2); // type: SlicedTube + // [slicedtube-class-9-end] + } + +} \ No newline at end of file diff --git a/doc/manual/manual/tubes/src.py b/doc/manual/manual/tubes/src.py new file mode 100644 index 000000000..23d27c3e6 --- /dev/null +++ b/doc/manual/manual/tubes/src.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python + +# Codac tests +# ---------------------------------------------------------------------------- +# \date 2026 +# \author Simon Rohou +# \copyright Copyright 2026 Codac Team +# \license GNU Lesser General Public License (LGPL) + +import sys, os +import unittest +import math +from codac import * + +class TestTubeManual(unittest.TestCase): + + def tests_TDomain_manual(test): + + # [tdomain-class-1-beg] + td0 = create_tdomain() # one unbounded slice [-oo,oo] + td1 = create_tdomain([0,1]) # one slice [0,1] + td2 = create_tdomain([0,1], 0.5, False) # [0,0.5],[0.5,1] + td3 = create_tdomain([0,1], 0.5, True) # [0],[0,0.5],[0.5],[0.5,1],[1] + # [tdomain-class-1-end] + + # [tdomain-class-2-beg] + td = create_tdomain([0,1], 0.5, True) + + dom = td.t0_tf() # [0,1] + n = td.nb_tslices() # 5 + m = td.nb_tubes() # 0 initially + + td.tslice(0.0) # [0] + td.tslice(0.1) # [0,0.5] + td.tslice(0.5) # [0.5] + td.tslice(0.6) # [0.5,1] + td.tslice(1.0) # [1] + # [tdomain-class-2-end] + + test.assertTrue(n == 5) + test.assertTrue(m == 0) + test.assertTrue(dom == Interval(0,1)) + + # [tdomain-class-3-beg] + td = create_tdomain() + td.sample(1.0, False) # [-oo,1],[1,oo] + td.sample(10.0, True) # [-oo,1],[1,10],[10],[10,oo] + # [tdomain-class-3-end] + + # [tdomain-class-4-beg] + td = create_tdomain([0,2], 1.0, False) # False: without gates + + x = SlicedTube(td, Interval(0,1)) + v = SlicedTube(td, Interval(-1,1)) + + print(x) # outputs [0,2]->[0,1], 2 slices + print(v) # outputs [0,2]->[0,1], 2 slices + + n = td.nb_tslices() # 2: [0,1],[1,2] + x.set([0.5,1], 1.3) # local update, will refine the partition at t=1.3 + m = td.nb_tslices() # now 4: [0,1],[1,1.3],[1.3],[1.3,2] + + print(x) # outputs [0,2]->[-1,1], 4 slices + print(v) # outputs [0,2]->[-1,1], 4 slices (v is also impacted by x.set(..)) + # [tdomain-class-4-end] + + test.assertTrue(n == 2) + test.assertTrue(m == 4) + + # [tdomain-class-5-beg] + td = create_tdomain([0,3], 1.0, False) + x = SlicedTube(td, Interval(0,1)) + + td.nb_tubes() # 1 + td.truncate([0.5,2.5]) + + print(td.t0_tf()) # [0.5,2.5] + print(x) # x now uses the truncated shared partition + # [tdomain-class-5-end] + + + def tests_SlicedTube_manual(test): + + fig = Figure2D("Tube", GraphicOutput.VIBES) + fig.set_window_properties([50,50],[800,400]) + + # [slicedtube-class-1-beg] + td = create_tdomain([0,10], 0.1, True) + + # Sliced tube from interval-type codomains + x = SlicedTube(td, Interval(-1,1)) + y = SlicedTube(td, IntervalVector(2)) + z = SlicedTube(td, IntervalMatrix(2,2)) + + # From an analytic function + t = ScalarVar() + f = AnalyticFunction([t], sin(t)) + xf = SlicedTube(td, f) + + # From an analytic trajectory or sampled trajectory + at = AnalyticTraj([0,10], AnalyticFunction([t],cos(t))) + st = AnalyticTraj([0,10], AnalyticFunction([t],cos(t)+t/10)).sampled(1e-2) + xu = SlicedTube(td,at) | SlicedTube(td,st) # union (hull) of tubes + + fig.plot_tube(xf, [Color.dark_blue(),Color.light_gray()]) + fig.plot_tube(xu, [Color.dark_blue(),Color.light_gray()]) + # [slicedtube-class-1-end] + + # [slicedtube-class-2-beg] + td = create_tdomain([0,3]) + x = SlicedTube(td, IntervalVector(2)) + x.set([[1,5],[-oo,2]], [0,1]) + x.set([[2,8],[-oo,3]], [1,2]) + x.set([[6,9],[-oo,4]], [2,3]) + + s0 = x.first_slice() + s1 = s0.next_slice() + + for s in x: + print(s.t0_tf(), s.codomain()) + # [slicedtube-class-2-end] + + # [slicedtube-class-4-beg] + td = create_tdomain([0,3], 1.0, False) + x = SlicedTube(td, Interval()) + + x.set([1,5], [0,1]) + x.set([2,8], [1,2]) + x.set([6,9], [2,3]) + + x(0.5) # [1,5] + x(1.5) # [2,8] + x([0,3]) # [1,9] + x(-1.0) # [-oo,oo] + + # No explicit gates: boundary values come from adjacent-slice intersections + x(1.0) # [2,5] + x(2.0) # [6,8] + x(3.0) # [6,9] + # [slicedtube-class-4-end] + + T = Interval(0,10) + td = create_tdomain(T, 1e-1) + + t = ScalarVar() + + z1 = AnalyticTraj(T, AnalyticFunction([t],cos(t))) + z2 = AnalyticTraj(T, AnalyticFunction([t],cos(t)+t/10)) + z3 = AnalyticTraj(T, AnalyticFunction([t],sin(t)+t/10)) + z4 = AnalyticTraj(T, AnalyticFunction([t],sin(t))).sampled(1e-2) + + x1 = SlicedTube(td, z1) + x2 = SlicedTube(td, z2) + x3 = SlicedTube(td, z3) + x4 = SlicedTube(td, z4) + + x = x1 | x2 | x3 | x4 + + # [slicedtube-class-5-beg] + v_t = [] + y = Interval(0,0.2) + x.invert(y,v_t) + + for t in v_t: + z = cart_prod(t,y) + DefaultFigure.draw_box(z, Color.red()) + # [slicedtube-class-5-end] + + # [slicedtube-class-6-beg] + td = create_tdomain([0,2], 1.0, False) + + x = SlicedTube(td, Interval(1,2)) + y = SlicedTube(td, Interval(-1,3)) + + z_add = x + y # addition of two tubes + z_mul = 2 * x # multiplication by a scalar/interval + z_hul = x | y # hull (union) of two tubes + z_int = x & y # intersection of two tubes + + u = sin(x) + exp(y) + # [slicedtube-class-6-end] + + # [slicedtube-class-7-beg] + A = SlicedTube(td, IntervalMatrix(2,2)) + b = SlicedTube(td, IntervalVector(2)) + + y = A * b + # [slicedtube-class-7-end] + + td = create_tdomain([0,2]) + x = SlicedTube(td, Interval()) + + # [slicedtube-class-8-beg] + x.inflate(0.2) # constant inflation + + rad = SampledTraj({0.0:0.1, 1.0:0.3, 2.0:0.2}) + x.inflate(rad) # time-varying inflation radius + # [slicedtube-class-8-end] + + td = create_tdomain() + x = SlicedTube(td, IntervalVector(3)) + + # [slicedtube-class-9-beg] + # Component and subvector extraction + x0 = x[0] + x12 = x.subvector(1,2) + # [slicedtube-class-9-end] + + pass + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/doc/manual/manual/tubes/tdomain.rst b/doc/manual/manual/tubes/tdomain.rst new file mode 100644 index 000000000..d65eebdc4 --- /dev/null +++ b/doc/manual/manual/tubes/tdomain.rst @@ -0,0 +1,189 @@ +.. _sec-domains-tubes-tdomain: + +The TDomain class +================= + + Main author: `Simon Rohou `_ + +A :class:`~codac.TDomain` is the temporal partition shared by one or more sliced tubes. +It stores an ordered list of :class:`~codac.TSlice` objects. Each stored element is either + +* a non-degenerate temporal slice :math:`[t_i,t_{i+1}]`, or +* a degenerate temporal slice :math:`[t_i,t_i]`, called a *gate*. + +Gates are optional. When they are explicitly represented, the partition may alternate between +``gate / slice / gate / slice / ...``. This is the structure created by +``create_tdomain(t0_tf, dt, True)``. + +Although optional, gates are useful when one needs to represent tube values explicitly at specific time instants. This is particularly relevant for guaranteed integration. + +Creating temporal domains +------------------------- + +A temporal domain can be created in three common ways: + +* from the default constructor helper ``create_tdomain()``; +* from a single time interval ``create_tdomain([t0,tf])``; +* from a sampled interval ``create_tdomain([t0,tf], dt, with_gates)``. + +The following examples show the expected structures for the sampled cases above, both with and without gates. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-1-beg] + :end-before: [tdomain-class-1-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-1-beg] + :end-before: [tdomain-class-1-end] + :dedent: 4 + + +Inspecting the partition +------------------------ + +A temporal domain exposes: + +* its global time interval ``t0_tf()``, +* the number of stored temporal elements ``nb_tslices()``, +* the number of attached tubes ``nb_tubes()``, +* the temporal slice containing a given time ``tslice(t)``, +* and a vector copy of the stored temporal slices ``tslices_vector()``. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-2-beg] + :end-before: [tdomain-class-2-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-2-beg] + :end-before: [tdomain-class-2-end] + :dedent: 4 + + +Note that ``tslice(t)`` returns the gate when ``t`` matches an explicit gate, and +otherwise returns the unique non-degenerate slice containing ``t``. + +Sampling and refinement +----------------------- + +The temporal partition can be refined dynamically with: + +* ``sample(t, with_gate=False)`` for one time value, +* ``sample([ta,tb], dt, with_gates=False)`` for repeated sampling over an interval. + +Sampling can occur: + +* inside the current domain, which splits an existing slice; +* outside the current domain, which extends the partition; +* with gates, which inserts a degenerate temporal slice at the sampling time. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-3-beg] + :end-before: [tdomain-class-3-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-3-beg] + :end-before: [tdomain-class-3-end] + :dedent: 4 + +One important implementation detail is that refinement preserves the attached tubes by cloning the +corresponding slice objects onto the new temporal elements. The new slices are then reattached to +the newly inserted ``TSlice`` iterators. + +Shared refinement across several tubes +-------------------------------------- + +One practical consequence of the shared-``TDomain`` design is that refining the time +partition from one tube automatically updates the slice structure seen by all the +other tubes attached to the same temporal domain. +The following code illustrates this effect: + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-4-beg] + :end-before: [tdomain-class-4-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-4-beg] + :end-before: [tdomain-class-4-end] + :dedent: 4 + +.. note:: + + This behavior is intentional: it keeps all related tubes on a consistent shared + temporal support, which greatly simplifies multi-tube operations such as + arithmetic combinations and contractions. + +Gates +----- + +Explicit gates are useful when you want to represent endpoint values separately from the interior +codomain of a slice. The current API provides: + +* ``all_gates_defined()`` to detect a fully gate-augmented partition, +* ``delete_gates()`` to remove all explicit gates. + +Therefore, a :math:`t`-domain built from ``create_tdomain([0,1], 0.5, True)`` contains five temporal elements and is a +typical example of a gate-augmented partition. +In this implementation, a gate is not a separate data type: it is simply a degenerate +:class:`~codac.TSlice`, i.e., an interval :math:`[t,t]`. + +Shared temporal structure +------------------------- + +A :class:`~codac.TDomain` is shared by all sliced tubes built from it. +As a consequence, structural operations such as ``truncate(...)`` affect all +attached tubes, and the number of attached tubes can be queried with +``nb_tubes()``. + +.. tabs:: + + .. group-tab:: Python + + .. literalinclude:: src.py + :language: py + :start-after: [tdomain-class-5-beg] + :end-before: [tdomain-class-5-end] + :dedent: 4 + + .. group-tab:: C++ + + .. literalinclude:: src.cpp + :language: c++ + :start-after: [tdomain-class-5-beg] + :end-before: [tdomain-class-5-end] + :dedent: 4 \ No newline at end of file diff --git a/doc/manual/manual/tubes/tube_integ_inf.png b/doc/manual/manual/tubes/tube_integ_inf.png new file mode 100644 index 000000000..c21d66e8a Binary files /dev/null and b/doc/manual/manual/tubes/tube_integ_inf.png differ diff --git a/doc/manual/manual/tubes/tube_lb_integral_slices.png b/doc/manual/manual/tubes/tube_lb_integral_slices.png new file mode 100644 index 000000000..8f106c7fa Binary files /dev/null and b/doc/manual/manual/tubes/tube_lb_integral_slices.png differ diff --git a/doc/manual/manual/tubes/tube_slices.png b/doc/manual/manual/tubes/tube_slices.png new file mode 100644 index 000000000..81acf65dc Binary files /dev/null and b/doc/manual/manual/tubes/tube_slices.png differ diff --git a/examples/00_graphics/graphic_examples.cpp b/examples/00_graphics/graphic_examples.cpp index 7c4fe8b07..90e7cd040 100644 --- a/examples/00_graphics/graphic_examples.cpp +++ b/examples/00_graphics/graphic_examples.cpp @@ -119,6 +119,6 @@ int main(){ ScalarVar t; // Fermat's spiral AnalyticFunction f1 ({t},{a*sqrt(t)*cos(t),a*sqrt(t)*sin(t)}); - AnalyticTraj traj4 (f1,{0,100}); + AnalyticTraj traj4 ({0,100},f1); fig4.draw_trajectory(traj4,{ColorMap::rainbow(), ".."}); } diff --git a/examples/00_graphics/graphic_examples.m b/examples/00_graphics/graphic_examples.m index 2190ec6fe..a8463267e 100644 --- a/examples/00_graphics/graphic_examples.m +++ b/examples/00_graphics/graphic_examples.m @@ -108,5 +108,5 @@ t = ScalarVar(); % Fermat's spiral f1 = AnalyticFunction({t},vec(a*sqrt(t)*cos(t),a*sqrt(t)*sin(t))); -traj4 = AnalyticTraj(f1,Interval(0,100)); +traj4 = AnalyticTraj(Interval(0,100),f1); fig4.draw_trajectory(traj4, StyleGradientProperties(ColorMap().rainbow(), "..")); \ No newline at end of file diff --git a/examples/00_graphics/graphic_examples.py b/examples/00_graphics/graphic_examples.py index 54a790913..060138e88 100644 --- a/examples/00_graphics/graphic_examples.py +++ b/examples/00_graphics/graphic_examples.py @@ -111,5 +111,5 @@ t=ScalarVar() # Fermat's spiral f1=AnalyticFunction([t], [a*sqrt(t)*cos(t),a*sqrt(t)*sin(t)]) -traj4=AnalyticTraj(f1, [0,100]) +traj4=AnalyticTraj([0,100],f1) fig4.draw_trajectory(traj4, StyleGradientProperties(ColorMap.rainbow(),"..")) \ No newline at end of file diff --git a/examples/04_explored_area/main.cpp b/examples/04_explored_area/main.cpp index 649c4690b..e1d646348 100644 --- a/examples/04_explored_area/main.cpp +++ b/examples/04_explored_area/main.cpp @@ -18,7 +18,7 @@ int main() }; Interval tdomain(0,5); - auto sampled_f = AnalyticTraj(f,tdomain).sampled(0.8); + auto sampled_f = AnalyticTraj(tdomain,f).sampled(0.8); sampled_f.set({0,-1}, 6.); // appending the position (0,-1) at t=6 VectorVar w(3); @@ -33,5 +33,5 @@ int main() DefaultFigure::pave({{-3,3},{-2,2}}, s_projh, 5e-2); DefaultFigure::draw_trajectory(sampled_f); - DefaultFigure::draw_trajectory(AnalyticTraj(f,tdomain), Color::dark_gray()); + DefaultFigure::draw_trajectory(AnalyticTraj(tdomain,f), Color::dark_gray()); } \ No newline at end of file diff --git a/examples/04_explored_area/main.py b/examples/04_explored_area/main.py index 2990a5022..2e6046b49 100644 --- a/examples/04_explored_area/main.py +++ b/examples/04_explored_area/main.py @@ -14,7 +14,7 @@ ) tdomain = [0,5] -sampled_f = AnalyticTraj(f,tdomain).sampled(0.8) +sampled_f = AnalyticTraj(tdomain,f).sampled(0.8) sampled_f.set([0,-1], 6.) # appending the position (0,-1) at t=6 w = VectorVar(3) @@ -29,4 +29,4 @@ DefaultFigure.pave([[-3,3],[-2,2]], s_projh, 5e-2) DefaultFigure.draw_trajectory(sampled_f) -DefaultFigure.draw_trajectory(AnalyticTraj(f,tdomain), Color.dark_gray()) \ No newline at end of file +DefaultFigure.draw_trajectory(AnalyticTraj(tdomain,f), Color.dark_gray()) \ No newline at end of file diff --git a/examples/07_centered_2D/main.cpp b/examples/07_centered_2D/main.cpp index 48ca64520..bfc069633 100644 --- a/examples/07_centered_2D/main.cpp +++ b/examples/07_centered_2D/main.cpp @@ -43,6 +43,6 @@ int main(){ time = time+dt; } - AnalyticTraj traj4 (f1,{0,100}); + AnalyticTraj traj4 ({0,100},f1); fig4.draw_trajectory(traj4,Color::black()); } diff --git a/python/codac/core/__init__.py b/python/codac/core/__init__.py index 7e528dae9..831311cc6 100644 --- a/python/codac/core/__init__.py +++ b/python/codac/core/__init__.py @@ -9,6 +9,11 @@ import sys import warnings +try: + import numpy as _np +except ImportError: + _np = None + def codac_error(message): print(f''' @@ -41,6 +46,12 @@ def codac_error(message): AnalyticTraj_Matrix, ) +_SAMPLED_TRAJ_TYPES = ( + SampledTraj_Scalar, + SampledTraj_Vector, + SampledTraj_Matrix, +) + def _is_scalar_expr_like(x): return isinstance(x, (int, float, Interval, ScalarVar, ScalarExpr)) @@ -307,17 +318,78 @@ def traj_cart_prod(*x): return traj_cart_prod_list([*x]) -def AnalyticTraj(f, t): +def _sampled_traj_type_and_value(x): + if isinstance(x, (int, float)): + return SampledTraj_Scalar, float + + if isinstance(x, Vector) \ + or (_np is not None and isinstance(x, _np.ndarray) and x.ndim == 1) \ + or (isinstance(x, (list, tuple)) and (len(x) == 0 or not isinstance(x[0], (list, tuple)))): + return SampledTraj_Vector, lambda y: y if isinstance(y, Vector) else Vector(y) + + if isinstance(x, Matrix) \ + or (_np is not None and isinstance(x, _np.ndarray) and x.ndim == 2) \ + or (isinstance(x, (list, tuple)) and len(x) > 0 and isinstance(x[0], (list, tuple))): + return SampledTraj_Matrix, lambda y: y if isinstance(y, Matrix) else Matrix(y) + + return None, None + + +def SampledTraj(x=None, y=None): + if y is None: + if x is None: + codac_error("SampledTraj: unable to deduce the trajectory type from an empty constructor") + + for cls in _SAMPLED_TRAJ_TYPES: + if isinstance(x, cls): + return cls(dict(x)) + + if isinstance(x, dict): + if not x: + codac_error("SampledTraj: unable to deduce the trajectory type from an empty map") + cls, cast = _sampled_traj_type_and_value(next(iter(x.values()))) + if cls is None: + codac_error("SampledTraj: wrong constructor argument") + return cls({float(t): cast(v) for t, v in x.items()}) + + codac_error("SampledTraj: wrong constructor argument") + + if len(y) == 0: + codac_error("SampledTraj: unable to deduce the trajectory type from empty samples") + + cls, cast = _sampled_traj_type_and_value(y[0]) + if cls is None: + codac_error("SampledTraj: unable to deduce the trajectory type from the provided samples") + + if cls is SampledTraj_Scalar: + return cls(list(x), [float(v) for v in y]) + + if cls is SampledTraj_Vector and _np is not None and isinstance(x, _np.ndarray) and isinstance(y, _np.ndarray): + return cls(x, y) + + return cls({float(t): cast(v) for t, v in zip(x, y)}) + + +def AnalyticTraj(t,f): + + if isinstance(t, _ANALYTIC_FUNCTION_TYPES) and not isinstance(f, _ANALYTIC_FUNCTION_TYPES): + warnings.warn( + "AnalyticTraj(f,t) is deprecated; use AnalyticTraj(t,f) instead.", + FutureWarning, + stacklevel=2 + ) + t,f = f,t + f = AnalyticFunction(f) if isinstance(f, AnalyticFunction_Scalar): - return AnalyticTraj_Scalar(f, t) + return AnalyticTraj_Scalar(t,f) if isinstance(f, AnalyticFunction_Vector): - return AnalyticTraj_Vector(f, t) + return AnalyticTraj_Vector(t,f) if isinstance(f, AnalyticFunction_Matrix): - return AnalyticTraj_Matrix(f, t) + return AnalyticTraj_Matrix(t,f) codac_error("AnalyticTraj: can only build this trajectory from an AnalyticFunction_[Scalar/Vector/Matrix]") @@ -335,16 +407,16 @@ def SlicedTube(x, y=None): y = AnalyticFunction(y) if isinstance(y, _ANALYTIC_FUNCTION_TYPES) else y - if isinstance(y, (Interval, AnalyticFunction_Scalar, SampledTraj_Scalar)): + if isinstance(y, (Interval, AnalyticFunction_Scalar, AnalyticTraj_Scalar, SampledTraj_Scalar)): return SlicedTube_Interval(x, y) - if isinstance(y, (IntervalVector, AnalyticFunction_Vector, SampledTraj_Vector)): + if isinstance(y, (IntervalVector, AnalyticFunction_Vector, AnalyticTraj_Vector, SampledTraj_Vector)): return SlicedTube_IntervalVector(x, y) - if isinstance(y, (IntervalMatrix, AnalyticFunction_Matrix, SampledTraj_Matrix)): + if isinstance(y, (IntervalMatrix, AnalyticFunction_Matrix, AnalyticTraj_Matrix, SampledTraj_Matrix)): return SlicedTube_IntervalMatrix(x, y) - codac_error("SlicedTube: can only build this tube from an AnalyticFunction_[Scalar/Vector/Matrix]") + codac_error("SlicedTube: wrong constructor argument") def fixpoint(contract, *x): diff --git a/python/src/codac2_py_matlab.h b/python/src/codac2_py_matlab.h index 9c53edb48..b128d2001 100644 --- a/python/src/codac2_py_matlab.h +++ b/python/src/codac2_py_matlab.h @@ -42,7 +42,7 @@ namespace codac2 } template - Index input_index(const I& x) + Index_type input_index(const I& x) { if constexpr(FOR_MATLAB) return x-1; @@ -57,7 +57,7 @@ namespace codac2 return x; } - inline auto convert_indices(const std::vector& indices) + inline std::vector convert_indices(const std::vector& indices) { if constexpr(FOR_MATLAB) { diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 65fe7acf2..d569f74bd 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -55,7 +55,8 @@ domains/zonotope/codac2_py_Parallelepiped.cpp domains/zonotope/codac2_py_Zonotope.cpp domains/tube/codac2_py_Slice.h - domains/tube/codac2_py_SlicedTube.cpp + domains/tube/codac2_py_SlicedTube.h + domains/tube/codac2_py_SlicedTube_operations.cpp domains/tube/codac2_py_TDomain.cpp domains/tube/codac2_py_TimePropag.cpp domains/tube/codac2_py_TSlice.cpp diff --git a/python/src/core/codac2_py_core.cpp b/python/src/core/codac2_py_core.cpp index 05f160098..370630623 100644 --- a/python/src/core/codac2_py_core.cpp +++ b/python/src/core/codac2_py_core.cpp @@ -21,6 +21,7 @@ #include "codac2_py_CtcInverse.h" #include "codac2_py_MatrixBlock.h" #include "codac2_py_Slice.h" +#include "codac2_py_SlicedTube.h" using namespace codac2; namespace py = pybind11; @@ -79,7 +80,11 @@ void export_TimePropag(py::module& m); void export_TSlice(py::module& m); py::class_ export_TubeBase(py::module& m); void export_tube_cart_prod(py::module& m); -void export_SlicedTube(py::module& m); +void export_SlicedTube_operations( + py::module& m, + py::class_,TubeBase>& py_SlicedTube_Interval, + py::class_,TubeBase>& py_SlicedTube_IntervalVector, + py::class_,TubeBase>& py_SlicedTube_IntervalMatrix); // functions void export_VarBase(py::module& m); @@ -248,7 +253,15 @@ PYBIND11_MODULE(_core, m) export_TDomain(m); export_TSlice(m); auto tube_base = export_TubeBase(m); - export_SlicedTube(m); + auto py_SlicedTube_Interval = export_SlicedTube(m, "SlicedTube_Interval"); + auto py_SlicedTube_IntervalVector = export_SlicedTube(m, "SlicedTube_IntervalVector"); + auto py_SlicedTube_IntervalMatrix = export_SlicedTube(m, "SlicedTube_IntervalMatrix"); + export_SlicedTube_operations( + m, + py_SlicedTube_Interval, + py_SlicedTube_IntervalVector, + py_SlicedTube_IntervalMatrix + ); export_tube_cart_prod(m); export_arithmetic_add(py_V, py_IV, py_M, py_IM, py_B, py_IB); diff --git a/python/src/core/domains/interval/codac2_py_IntervalMatrixBase.h b/python/src/core/domains/interval/codac2_py_IntervalMatrixBase.h index 622d22db5..84059ee33 100644 --- a/python/src/core/domains/interval/codac2_py_IntervalMatrixBase.h +++ b/python/src/core/domains/interval/codac2_py_IntervalMatrixBase.h @@ -70,36 +70,61 @@ void export_IntervalMatrixBase(py::module& m, py::class_& pyclass) .def("diam", [](const S& x) { return x.diam(); }, MATRIXBASE_ADDONS_INTERVALMATRIXBASE_AUTO_DIAM_CONST) - .def("min_rad", [](const S& x) { return x.min_rad(); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MIN_RAD_CONST) + .def("min_rad", [](const S& x, const std::vector& among_indices) + { + return x.min_rad( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices)); + }, + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MIN_RAD_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("max_rad", [](const S& x) { return x.max_rad(); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MAX_RAD_CONST) + .def("max_rad", [](const S& x, const std::vector& among_indices) + { + return x.max_rad( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices)); + }, + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MAX_RAD_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("min_diam", [](const S& x) { return x.min_diam(); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MIN_DIAM_CONST) + .def("min_diam", [](const S& x, const std::vector& among_indices) + { + return x.min_diam( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices)); + }, + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MIN_DIAM_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("max_diam", [](const S& x) { return x.max_diam(); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MAX_DIAM_CONST) + .def("max_diam", [](const S& x, const std::vector& among_indices) + { + return x.max_diam( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices)); + }, + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_DOUBLE_MAX_DIAM_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("min_diam_index", [](const S& x) + .def("min_diam_index", [](const S& x, const std::vector& among_indices) { - return matlab::output_index(x.min_diam_index()); + return matlab::output_index(x.min_diam_index( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices))); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_MIN_DIAM_INDEX_CONST) + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_MIN_DIAM_INDEX_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("max_diam_index", [](const S& x) + .def("max_diam_index", [](const S& x, const std::vector& among_indices) { - return matlab::output_index(x.max_diam_index()); + return matlab::output_index(x.max_diam_index( + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices))); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_MAX_DIAM_INDEX_CONST) + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_MAX_DIAM_INDEX_CONST_VECTOR_INDEX_REF_CONST, + "among_indices"_a=std::vector()) - .def("extr_diam_index", [](const S& x, bool min) + .def("extr_diam_index", [](const S& x, bool min, const std::vector& among_indices) { - return matlab::output_index(x.extr_diam_index(min)); + return matlab::output_index(x.extr_diam_index(min, + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices))); }, - MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_EXTR_DIAM_INDEX_BOOL_CONST, - "min"_a) + MATRIXBASE_ADDONS_INTERVALMATRIXBASE_INDEX_EXTR_DIAM_INDEX_BOOL_CONST_VECTOR_INDEX_REF_CONST, + "min"_a, "among_indices"_a=std::vector()) .def("__contains__", [](const S& x, const V& y) { return x.contains(y); }, MATRIXBASE_ADDONS_INTERVALMATRIXBASE_BOOL_CONTAINS_CONST_MATRIXBASE_OTHERDERIVED_REF_CONST) @@ -168,9 +193,14 @@ void export_IntervalMatrixBase(py::module& m, py::class_& pyclass) MATRIX_ADDONS_INTERVALMATRIXBASE_AUTO_BISECT_INDEX_FLOAT_CONST, "i"_a, "ratio"_a = 0.49) - .def("bisect_largest", [](const S& x, double ratio = 0.49) { return x.bisect_largest(ratio); }, - MATRIX_ADDONS_INTERVALMATRIXBASE_AUTO_BISECT_LARGEST_FLOAT_CONST, - "ratio"_a = 0.49) + .def("bisect_largest", + [](const S& x, double ratio, const std::vector& among_indices) + { + return x.bisect_largest(ratio, + among_indices.empty() ? among_indices : matlab::convert_indices(among_indices)); + }, + MATRIX_ADDONS_INTERVALMATRIXBASE_AUTO_BISECT_LARGEST_DOUBLE_CONST_VECTOR_INDEX_REF_CONST, + "ratio"_a = 0.49, "among_indices"_a=std::vector()) ; if constexpr(!FOR_MATLAB) diff --git a/python/src/core/domains/tube/codac2_py_SlicedTube.cpp b/python/src/core/domains/tube/codac2_py_SlicedTube.cpp deleted file mode 100644 index 9182b381b..000000000 --- a/python/src/core/domains/tube/codac2_py_SlicedTube.cpp +++ /dev/null @@ -1,726 +0,0 @@ -/** - * Codac binding (core) - * ---------------------------------------------------------------------------- - * \date 2024 - * \author Simon Rohou - * \copyright Copyright 2024 Codac Team - * \license GNU Lesser General Public License (LGPL) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "codac2_py_matlab.h" -#include "codac2_py_SlicedTubeBase_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): -#include "codac2_py_SlicedTube_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): -#include "codac2_py_SlicedTube_operations_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): - -using namespace std; -using namespace codac2; -namespace py = pybind11; -using namespace pybind11::literals; - - -template -py::class_,TubeBase> _export_SlicedTube(py::module& m, const std::string& name) -{ - py::class_,TubeBase> exported_slicedtube_class(m, name.c_str(), SLICEDTUBEBASE_MAIN); - exported_slicedtube_class - - .def(py::init&,const T&>(), - SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_T_REF, - "tdomain"_a, "codomain"_a) - - .def(py::init&,const AnalyticFunction::Type>&>(), - SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_ANALYTICFUNCTION_TYPENAME_EXPRTYPE_T_TYPE_REF, - "tdomain"_a, "f"_a) - - .def(py::init&,const SampledTraj::Type::Scalar>&>(), - SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_SAMPLEDTRAJ_V_REF, - "tdomain"_a, "x"_a) - - .def(py::init&>(), - SLICEDTUBE_T_SLICEDTUBE_CONST_SLICEDTUBE_T_REF, - "x"_a) - - .def("__iter__", [](const SlicedTube& x) - { - return py::make_iterator(x.begin(), x.end()); - }, - py::keep_alive<0,1>()) // essential: keep object alive while iterator exists - - // From SlicedTubeBase class: - .def("nb_slices", &SlicedTube::nb_slices, - SIZET_SLICEDTUBEBASE_NB_SLICES_CONST) - - .def("size", &SlicedTube::size, - INDEX_SLICEDTUBE_T_SIZE_CONST) - - .def("volume", &SlicedTube::volume, - DOUBLE_SLICEDTUBE_T_VOLUME_CONST) - - .def("first_slice", [](SlicedTube& x) -> Slice& - { - return *x.first_slice(); - }, - py::return_value_policy::reference, - SHARED_PTR_SLICE_T_SLICEDTUBE_T_FIRST_SLICE) - - .def("last_slice", [](SlicedTube& x) -> Slice& - { - return *x.last_slice(); - }, - py::return_value_policy::reference, - SHARED_PTR_SLICE_T_SLICEDTUBE_T_LAST_SLICE) - - .def("slice", [](SlicedTube& x, std::shared_ptr it) -> Slice& - { - return *x.slice(it); - }, - py::return_value_policy::reference, - SHARED_PTR_SLICE_T_SLICEDTUBE_T_SLICE_SHARED_PTR_TSLICE, - "it"_a) - - .def("is_empty", &SlicedTube::is_empty, - BOOL_SLICEDTUBE_T_IS_EMPTY_CONST) - - .def("is_unbounded", &SlicedTube::is_unbounded, - BOOL_SLICEDTUBE_T_IS_UNBOUNDED_CONST) - - .def("codomain", &SlicedTube::codomain, - T_SLICEDTUBE_T_CODOMAIN_CONST) - - .def("__call__", [](const SlicedTube& x, const Interval& t) - { - return x(t); - }, - T_SLICEDTUBE_T_OPERATORCALL_CONST_INTERVAL_REF_CONST, - "t"_a) - - .def("enclosed_bounds", &SlicedTube::enclosed_bounds, - PAIR_TT_SLICEDTUBE_T_ENCLOSED_BOUNDS_CONST_INTERVAL_REF_CONST, - "t"_a) - - .def("invert", (Interval (SlicedTube::*)(const T&,const Interval&) const) &SlicedTube::invert, - INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "t"_a=Interval()) - - .def("invert", [](const SlicedTube& x, const T& y, py::list& v_t, const Interval& t) - { - vector vector_t; - x.invert(y, vector_t, t); - v_t.clear(); - for(const auto& ti : vector_t) - v_t.append(ti); - }, - INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "v_t"_a, "t"_a=Interval()) - - .def("set", (void (SlicedTube::*)(const T&)) &SlicedTube::set, - VOID_SLICEDTUBE_T_SET_CONST_T_REF, - "codomain"_a) - - .def("set", (void (SlicedTube::*)(const T&,double)) &SlicedTube::set, - VOID_SLICEDTUBE_T_SET_CONST_T_REF_DOUBLE, - "codomain"_a, "t"_a) - - .def("set", (void (SlicedTube::*)(const T&,const Interval&)) &SlicedTube::set, - VOID_SLICEDTUBE_T_SET_CONST_T_REF_CONST_INTERVAL_REF, - "codomain"_a, "t"_a) - - .def("set_ith_slice", &SlicedTube::set_ith_slice, - VOID_SLICEDTUBE_T_SET_ITH_SLICE_CONST_T_REF_INDEX, - "codomain"_a, "i"_a) - - .def("inflate", (const SlicedTube& (SlicedTube::*)(const double&)) &SlicedTube::inflate, - CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_V_REF, - "rad"_a) - - .def("inflate", (const SlicedTube& (SlicedTube::*)(const SampledTraj&)) &SlicedTube::inflate, - CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_SAMPLEDTRAJ_V_REF, - "rad"_a) - ; - - if constexpr(std::is_same_v) - { - exported_slicedtube_class - - .def("inflate", (const SlicedTube& (SlicedTube::*)(const Vector&)) &SlicedTube::inflate, - CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_V_REF, - "rad"_a) - - .def("inflate", (const SlicedTube& (SlicedTube::*)(const SampledTraj&)) &SlicedTube::inflate, - CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_SAMPLEDTRAJ_V_REF, - "rad"_a) - ; - } - - exported_slicedtube_class - - .def(py::self == py::self, - BOOL_SLICEDTUBE_T_OPERATOREQ_CONST_SLICEDTUBE_REF_CONST, - "x"_a) - - .def(py::self != py::self, - "x"_a) - - .def(py::self &= py::self, - SLICEDTUBE_SLICEDTUBE_T_OPERATORINTEREQ_CONST_SLICEDTUBE_REF, - "x"_a) - - // For MATLAB compatibility - .def("self_inter", &SlicedTube::operator&=, - SLICEDTUBE_SLICEDTUBE_T_OPERATORINTEREQ_CONST_SLICEDTUBE_REF, - "x"_a) - - .def("as_function", &SlicedTube::as_function, - ANALYTICFUNCTION_TYPENAME_EXPRTYPE_T_TYPE_SLICEDTUBE_T_AS_FUNCTION_CONST) - - .def("all_reals_value", &SlicedTube::all_reals_value, - T_SLICEDTUBE_T_ALL_REALS_VALUE_CONST) - - .def("empty_value", &SlicedTube::empty_value, - T_SLICEDTUBE_T_EMPTY_VALUE_CONST) - - .def("__repr__", [](const SlicedTube& x) { - std::ostringstream stream; - stream << x; - return string(stream.str()); - }, - OSTREAM_REF_OPERATOROUT_OSTREAM_REF_CONST_SLICEDTUBE_T_REF) - ; - - if constexpr(std::is_same_v || std::is_same_v) - { - exported_slicedtube_class - - .def("__call__", [](const SlicedTube& x, const Interval& t, const SlicedTube& v) - { - return x(t,v); - }, - T_SLICEDTUBE_T_OPERATORCALL_CONST_INTERVAL_REF_CONST_SLICEDTUBE_T_REF_CONST, - "t"_a, "v"_a) - - .def("integral", (T (SlicedTube::*)(const Interval&) const) &SlicedTube::integral, - T_SLICEDTUBE_T_INTEGRAL_CONST_INTERVAL_REF_CONST, - "t"_a) - - .def("integral", (T (SlicedTube::*)(const Interval&,const Interval&) const) &SlicedTube::integral, - T_SLICEDTUBE_T_INTEGRAL_CONST_INTERVAL_REF_CONST_INTERVAL_REF_CONST, - "t1"_a, "t2"_a) - - .def("partial_integral", (std::pair (SlicedTube::*)(const Interval&) const) &SlicedTube::partial_integral, - PAIR_TT_SLICEDTUBE_T_PARTIAL_INTEGRAL_CONST_INTERVAL_REF_CONST, - "t"_a) - - .def("partial_integral", (std::pair (SlicedTube::*)(const Interval&,const Interval&) const) &SlicedTube::partial_integral, - PAIR_TT_SLICEDTUBE_T_PARTIAL_INTEGRAL_CONST_INTERVAL_REF_CONST_INTERVAL_REF_CONST, - "t1"_a, "t2"_a) - - .def("primitive", (SlicedTube (SlicedTube::*)() const) &SlicedTube::primitive, - SLICEDTUBE_T_SLICEDTUBE_T_PRIMITIVE_CONST) - - .def("primitive", (SlicedTube (SlicedTube::*)(const T&) const) &SlicedTube::primitive, - SLICEDTUBE_T_SLICEDTUBE_T_PRIMITIVE_CONST_T_REF_CONST, - "x0"_a) - - .def("invert", (Interval (SlicedTube::*)(const T&,const Interval&) const) &SlicedTube::invert, - INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "t"_a=Interval()) - - .def("invert", (void (SlicedTube::*)(const T&,std::vector&,const Interval&) const) &SlicedTube::invert, - VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "v_t"_a, "t"_a=Interval()) - - .def("invert", (Interval (SlicedTube::*)(const T&,const SlicedTube&,const Interval&) const) &SlicedTube::invert, - INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "v"_a, "t"_a=Interval()) - - .def("invert", (void (SlicedTube::*)(const T&,std::vector&,const SlicedTube&,const Interval&) const) &SlicedTube::invert, - VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF_CONST, - "y"_a, "v_t"_a, "v"_a, "t"_a=Interval()) - - ; - } - - if constexpr(std::is_same_v) - { - exported_slicedtube_class - - .def( - #if FOR_MATLAB - "get_item" - #else - "__getitem__" - #endif - , - [](const SlicedTube& x, Index_type i) -> SlicedTube - { - matlab::test_integer(i); - return x[matlab::input_index(i)]; - }, - SLICEDTUBE_INTERVAL_SLICEDTUBE_T_OPERATORCOMPO_INDEX_CONST, - "i"_a) - - .def("get_item_0", - [](const SlicedTube& x, Index_type i) -> SlicedTube - { - matlab::test_integer(i); - return x[i]; - }, - SLICEDTUBE_INTERVAL_SLICEDTUBE_T_OPERATORCOMPO_INDEX_CONST, - "i"_a) - - .def("subvector", - [](const SlicedTube& x, Index_type i, Index_type j) -> SlicedTube - { - matlab::test_integer(i,j); - return x.subvector(matlab::input_index(i),matlab::input_index(j)); - }, - SLICEDTUBE_INTERVALVECTOR_SLICEDTUBE_T_SUBVECTOR_INDEX_INDEX_CONST, - "i"_a, "j"_a) - - .def("subvector_0", - [](const SlicedTube& x, Index_type i, Index_type j) -> SlicedTube - { - matlab::test_integer(i,j); - return x.subvector(i,j); - }, - SLICEDTUBE_INTERVALVECTOR_SLICEDTUBE_T_SUBVECTOR_INDEX_INDEX_CONST, - "i"_a, "j"_a) - ; - } - - if constexpr(std::is_same_v) - { - exported_slicedtube_class - .def("mid", &SlicedTube::mid, - AUTO_SLICEDTUBE_T_MID_CONST); - } - - if constexpr(std::is_same_v) - { - exported_slicedtube_class - .def("mid", &SlicedTube::mid, - AUTO_SLICEDTUBE_T_MID_CONST); - } - - if constexpr(std::is_same_v) - { - exported_slicedtube_class - .def("mid", &SlicedTube::mid, - AUTO_SLICEDTUBE_T_MID_CONST); - } - - return exported_slicedtube_class; -} - -template -void add_tube_operators(py::class_,TubeBase>& pyclass) -{ - pyclass - - .def("__add__", [](py::object& x1) { return cast>(x1); }, - CONST_SLICEDTUBE_T_REF_OPERATORPLUS_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__add__", [](py::object& x1, py::object& x2) { return cast>(x1)+cast>(x2); }, - SLICEDTUBE_T_OPERATORPLUS_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__add__", [](py::object& x1, const T& x2) { return cast>(x1)+x2; }, - SLICEDTUBE_T_OPERATORPLUS_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, - py::is_operator()) - - .def("__radd__", [](py::object& x2, const T& x1) { return x1+cast>(x2); }, - SLICEDTUBE_T_OPERATORPLUS_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__sub__", [](py::object& x1) { return -cast>(x1); }, - SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__sub__", [](py::object& x1, py::object& x2) { return cast>(x1)-cast>(x2); }, - SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__sub__", [](py::object& x1, const T& x2) { return cast>(x1)-x2; }, - SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, - py::is_operator()) - - .def("__rsub__", [](py::object& x2, const T& x1) { return x1-cast>(x2); }, - SLICEDTUBE_T_OPERATORMINUS_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__mul__", [](py::object& x1, const Interval& x2) { return x2*cast>(x1); }, - SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF, - py::is_operator()) - - .def("__rmul__", [](py::object& x2, const Interval& x1) { return x1*cast>(x2); }, - SLICEDTUBE_T_OPERATORMUL_CONST_INTERVAL_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__mul__", [](const Interval& x1, py::object& x2) { return x1*cast>(x2); }, - SLICEDTUBE_T_OPERATORMUL_CONST_INTERVAL_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def("__truediv__", [](py::object& x1, const Interval& x2) { return cast>(x1)/x2; }, - SLICEDTUBE_T_OPERATORDIV_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, - py::is_operator()) - ; -} - - -void export_SlicedTube(py::module& m) -{ - auto py_SlicedTube_Interval = _export_SlicedTube(m, "SlicedTube_Interval"); - auto py_SlicedTube_IntervalVector = _export_SlicedTube(m, "SlicedTube_IntervalVector"); - auto py_SlicedTube_IntervalMatrix = _export_SlicedTube(m, "SlicedTube_IntervalMatrix"); - - add_tube_operators(py_SlicedTube_Interval); - - py_SlicedTube_Interval - - .def(py::self * py::self, - SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def(py::self / py::self, - SLICEDTUBE_T_OPERATORDIV_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def(double() / py::self, - SLICEDTUBE_T_OPERATORDIV_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - ; - - add_tube_operators(py_SlicedTube_IntervalMatrix); - add_tube_operators(py_SlicedTube_IntervalVector); - py_SlicedTube_IntervalMatrix - - .def("__mul__", [](const SlicedTube& x1, const SlicedTube& x2) { return x1*x2; }, - SLICEDTUBE_INTERVALVECTOR_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALVECTOR_REF, - py::is_operator()) - - .def("__mul__", [](const SlicedTube& x1, const SlicedTube& x2) { return x1*x2; }, - SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - ; - - m - - .def("sqr", (SlicedTube (*)(const SlicedTube&)) &codac2::sqr, - SLICEDTUBE_INTERVAL_SQR_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sqrt", (SlicedTube (*)(const SlicedTube&)) &codac2::sqrt, - SLICEDTUBE_INTERVAL_SQRT_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("pow", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::pow, - SLICEDTUBE_INTERVAL_POW_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - //.def("root", (SlicedTube (*)(const SlicedTube&,int)) &codac2::root, - // SAMPLEDTRAJ_DOUBLE_ROOT_CONST_SAMPLEDTRAJ_DOUBLE_REF_INT, - // "x1"_a, "x2"_a, py::is_operator()) - - .def("exp", (SlicedTube (*)(const SlicedTube&)) &codac2::exp, - SLICEDTUBE_INTERVAL_EXP_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("log", (SlicedTube (*)(const SlicedTube&)) &codac2::log, - SLICEDTUBE_INTERVAL_LOG_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("cos", (SlicedTube (*)(const SlicedTube&)) &codac2::cos, - SLICEDTUBE_INTERVAL_COS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sin", (SlicedTube (*)(const SlicedTube&)) &codac2::sin, - SLICEDTUBE_INTERVAL_SIN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("tan", (SlicedTube (*)(const SlicedTube&)) &codac2::tan, - SLICEDTUBE_INTERVAL_TAN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("acos", (SlicedTube (*)(const SlicedTube&)) &codac2::acos, - SLICEDTUBE_INTERVAL_ACOS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("asin", (SlicedTube (*)(const SlicedTube&)) &codac2::asin, - SLICEDTUBE_INTERVAL_ASIN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atan", (SlicedTube (*)(const SlicedTube&)) &codac2::atan, - SLICEDTUBE_INTERVAL_ATAN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atan2", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::atan2, - SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("atan2", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::atan2, - SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("atan2", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::atan2, - SLICEDTUBE_INTERVAL_ATAN2_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("cosh", (SlicedTube (*)(const SlicedTube&)) &codac2::cosh, - SLICEDTUBE_INTERVAL_COSH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sinh", (SlicedTube (*)(const SlicedTube&)) &codac2::sinh, - SLICEDTUBE_INTERVAL_SINH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("tanh", (SlicedTube (*)(const SlicedTube&)) &codac2::tanh, - SLICEDTUBE_INTERVAL_TANH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("acosh", (SlicedTube (*)(const SlicedTube&)) &codac2::acosh, - SLICEDTUBE_INTERVAL_ACOSH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("asinh", (SlicedTube (*)(const SlicedTube&)) &codac2::asinh, - SLICEDTUBE_INTERVAL_ASINH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atanh", (SlicedTube (*)(const SlicedTube&)) &codac2::atanh, - SLICEDTUBE_INTERVAL_ATANH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("abs", (SlicedTube (*)(const SlicedTube&)) &codac2::abs, - SLICEDTUBE_INTERVAL_ABS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("min", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::min, - SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("min", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::min, - SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("min", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::min, - SLICEDTUBE_INTERVAL_MIN_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::max, - SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::max, - SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::max, - SLICEDTUBE_INTERVAL_MAX_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("sign", (SlicedTube (*)(const SlicedTube&)) &codac2::sign, - SLICEDTUBE_INTERVAL_SIGN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("integer", (SlicedTube (*)(const SlicedTube&)) &codac2::integer, - SLICEDTUBE_INTERVAL_INTEGER_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("floor", (SlicedTube (*)(const SlicedTube&)) &codac2::floor, - SLICEDTUBE_INTERVAL_FLOOR_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("ceil", (SlicedTube (*)(const SlicedTube&)) &codac2::ceil, - SLICEDTUBE_INTERVAL_CEIL_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - ; -} - - - -#if 0 - -// Keeping the following just in case... -// Equivalent to the previous block of code, but using py::object for tubes - -void export_SlicedTube(py::module& m) -{ - auto py_SlicedTube_Interval = _export_SlicedTube(m, "SlicedTube_Interval"); - auto py_SlicedTube_IntervalVector = _export_SlicedTube(m, "SlicedTube_IntervalVector"); - auto py_SlicedTube_IntervalMatrix = _export_SlicedTube(m, "SlicedTube_IntervalMatrix"); - - add_tube_operators(py_SlicedTube_Interval); - - py_SlicedTube_Interval - - .def(py::self * py::self, - SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def(py::self / py::self, - SLICEDTUBE_T_OPERATORDIV_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - .def(double() / py::self, - SLICEDTUBE_T_OPERATORDIV_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - ; - - add_tube_operators(py_SlicedTube_IntervalMatrix); - add_tube_operators(py_SlicedTube_IntervalVector); - py_SlicedTube_IntervalMatrix - - .def("__mul__", [](py::object& x1, py::object& x2) { return cast>(x1)*cast>(x2); }, - SLICEDTUBE_INTERVALVECTOR_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALVECTOR_REF, - py::is_operator()) - - .def("__mul__", [](py::object& x1, py::object& x2) { return cast>(x1)*cast>(x2); }, - SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, - py::is_operator()) - - ; - - m - - .def("sqr", [](py::object& x1) { return codac2::sqr(cast>(x1)); }, - SLICEDTUBE_INTERVAL_SQR_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sqrt", [](py::object& x1) { return codac2::sqrt(cast>(x1)); }, - SLICEDTUBE_INTERVAL_SQRT_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("pow", [](py::object& x1, const Interval& x2) { return codac2::pow(cast>(x1),x2); }, - SLICEDTUBE_INTERVAL_POW_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - //.def("root", (SlicedTube (*)(const SlicedTube&,int)) &codac2::root, - // SAMPLEDTRAJ_DOUBLE_ROOT_CONST_SAMPLEDTRAJ_DOUBLE_REF_INT, - // "x1"_a, "x2"_a, py::is_operator()) - - .def("exp", [](py::object& x1) { return codac2::exp(cast>(x1)); }, - SLICEDTUBE_INTERVAL_EXP_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("log", [](py::object& x1) { return codac2::log(cast>(x1)); }, - SLICEDTUBE_INTERVAL_LOG_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("cos", [](py::object& x1) { return codac2::cos(cast>(x1)); }, - SLICEDTUBE_INTERVAL_COS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sin", [](py::object& x1) { return codac2::sin(cast>(x1)); }, - SLICEDTUBE_INTERVAL_SIN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("tan", [](py::object& x1) { return codac2::tan(cast>(x1)); }, - SLICEDTUBE_INTERVAL_TAN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("acos", [](py::object& x1) { return codac2::acos(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ACOS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("asin", [](py::object& x1) { return codac2::asin(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ASIN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atan", [](py::object& x1) { return codac2::atan(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ATAN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atan2", [](py::object& x1, py::object& x2) { return codac2::atan2(cast>(x1),cast>(x2)); }, - SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("atan2", [](py::object& x1, const Interval& x2) { return codac2::atan2(cast>(x1),x2); }, - SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("atan2", [](const Interval& x1, py::object& x2) { return codac2::atan2(x1,cast>(x2)); }, - SLICEDTUBE_INTERVAL_ATAN2_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("cosh", [](py::object& x1) { return codac2::cosh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_COSH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("sinh", [](py::object& x1) { return codac2::sinh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_SINH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("tanh", [](py::object& x1) { return codac2::tanh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_TANH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("acosh", [](py::object& x1) { return codac2::acosh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ACOSH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("asinh", [](py::object& x1) { return codac2::asinh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ASINH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("atanh", [](py::object& x1) { return codac2::atanh(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ATANH_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("abs", [](py::object& x1) { return codac2::abs(cast>(x1)); }, - SLICEDTUBE_INTERVAL_ABS_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("min", [](py::object& x1, py::object& x2) { return codac2::min(cast>(x1),cast>(x2)); }, - SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("min", [](py::object& x1, const Interval& x2) { return codac2::min(cast>(x1),x2); }, - SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("min", [](const Interval& x1, py::object& x2) { return codac2::min(x1,cast>(x2)); }, - SLICEDTUBE_INTERVAL_MIN_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", [](py::object& x1, py::object& x2) { return codac2::max(cast>(x1),cast>(x2)); }, - SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", [](py::object& x1, const Interval& x2) { return codac2::max(cast>(x1),x2); }, - SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("max", [](const Interval& x1, py::object& x2) { return codac2::max(x1,cast>(x2)); }, - SLICEDTUBE_INTERVAL_MAX_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, "x2"_a, py::is_operator()) - - .def("sign", [](py::object& x1) { return codac2::sign(cast>(x1)); }, - SLICEDTUBE_INTERVAL_SIGN_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("integer", [](py::object& x1) { return codac2::integer(cast>(x1)); }, - SLICEDTUBE_INTERVAL_INTEGER_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("floor", [](py::object& x1) { return codac2::floor(cast>(x1)); }, - SLICEDTUBE_INTERVAL_FLOOR_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - .def("ceil", [](py::object& x1) { return codac2::ceil(cast>(x1)); }, - SLICEDTUBE_INTERVAL_CEIL_CONST_SLICEDTUBE_INTERVAL_REF, - "x1"_a, py::is_operator()) - - ; -} -#endif \ No newline at end of file diff --git a/python/src/core/domains/tube/codac2_py_SlicedTube.h b/python/src/core/domains/tube/codac2_py_SlicedTube.h new file mode 100644 index 000000000..85bfc96e4 --- /dev/null +++ b/python/src/core/domains/tube/codac2_py_SlicedTube.h @@ -0,0 +1,328 @@ +/** + * Codac binding (core) + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "codac2_py_matlab.h" +#include "codac2_py_SlicedTubeBase_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac2_py_SlicedTube_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): +#include "codac2_py_SlicedTube_operations_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py): + +using namespace std; +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +template +py::class_,TubeBase> export_SlicedTube(py::module& m, const std::string& name) +{ + py::class_,TubeBase> exported_slicedtube_class(m, name.c_str(), SLICEDTUBEBASE_MAIN); + exported_slicedtube_class + + .def(py::init&,const T&>(), + SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_T_REF, + "tdomain"_a, "codomain"_a) + + .def(py::init&,const AnalyticFunction::Type>&>(), + SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_ANALYTICFUNCTION_TYPENAME_EXPRTYPE_T_TYPE_REF, + "tdomain"_a, "f"_a) + + .def(py::init&,const AnalyticTraj::Type>&>(), + SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_ANALYTICTRAJ_V_REF, + "tdomain"_a, "x"_a) + + .def(py::init&,const SampledTraj::Type::Scalar>&>(), + SLICEDTUBE_T_SLICEDTUBE_CONST_SHARED_PTR_TDOMAIN_REF_CONST_SAMPLEDTRAJ_V_REF, + "tdomain"_a, "x"_a) + + .def(py::init&>(), + SLICEDTUBE_T_SLICEDTUBE_CONST_SLICEDTUBE_T_REF, + "x"_a) + + .def("__iter__", [](const SlicedTube& x) + { + return py::make_iterator(x.begin(), x.end()); + }, + py::keep_alive<0,1>()) // essential: keep object alive while iterator exists + + // From SlicedTubeBase class: + .def("nb_slices", &SlicedTube::nb_slices, + SIZET_SLICEDTUBEBASE_NB_SLICES_CONST) + + .def("size", &SlicedTube::size, + INDEX_SLICEDTUBE_T_SIZE_CONST) + + .def("volume", &SlicedTube::volume, + DOUBLE_SLICEDTUBE_T_VOLUME_CONST) + + .def("first_slice", [](SlicedTube& x) -> Slice& + { + return *x.first_slice(); + }, + py::return_value_policy::reference, + SHARED_PTR_SLICE_T_SLICEDTUBE_T_FIRST_SLICE) + + .def("last_slice", [](SlicedTube& x) -> Slice& + { + return *x.last_slice(); + }, + py::return_value_policy::reference, + SHARED_PTR_SLICE_T_SLICEDTUBE_T_LAST_SLICE) + + .def("slice", [](SlicedTube& x, std::shared_ptr it) -> Slice& + { + return *x.slice(it); + }, + py::return_value_policy::reference, + SHARED_PTR_SLICE_T_SLICEDTUBE_T_SLICE_SHARED_PTR_TSLICE, + "it"_a) + + .def("is_empty", &SlicedTube::is_empty, + BOOL_SLICEDTUBE_T_IS_EMPTY_CONST) + + .def("is_unbounded", &SlicedTube::is_unbounded, + BOOL_SLICEDTUBE_T_IS_UNBOUNDED_CONST) + + .def("codomain", &SlicedTube::codomain, + T_SLICEDTUBE_T_CODOMAIN_CONST) + + .def("__call__", [](const SlicedTube& x, const Interval& t) + { + return x(t); + }, + T_SLICEDTUBE_T_OPERATORCALL_CONST_INTERVAL_REF_CONST, + "t"_a) + + .def("enclosed_bounds", &SlicedTube::enclosed_bounds, + PAIR_TT_SLICEDTUBE_T_ENCLOSED_BOUNDS_CONST_INTERVAL_REF_CONST, + "t"_a) + + .def("set", (void (SlicedTube::*)(const T&)) &SlicedTube::set, + VOID_SLICEDTUBE_T_SET_CONST_T_REF, + "codomain"_a) + + .def("set", (void (SlicedTube::*)(const T&,double)) &SlicedTube::set, + VOID_SLICEDTUBE_T_SET_CONST_T_REF_DOUBLE, + "codomain"_a, "t"_a) + + .def("set", (void (SlicedTube::*)(const T&,const Interval&)) &SlicedTube::set, + VOID_SLICEDTUBE_T_SET_CONST_T_REF_CONST_INTERVAL_REF, + "codomain"_a, "t"_a) + + .def("set_ith_slice", &SlicedTube::set_ith_slice, + VOID_SLICEDTUBE_T_SET_ITH_SLICE_CONST_T_REF_INDEX, + "codomain"_a, "i"_a) + + .def("inflate", (const SlicedTube& (SlicedTube::*)(const double&)) &SlicedTube::inflate, + CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_V_REF, + "rad"_a) + + .def("inflate", (const SlicedTube& (SlicedTube::*)(const SampledTraj&)) &SlicedTube::inflate, + CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_SAMPLEDTRAJ_V_REF, + "rad"_a) + ; + + if constexpr(std::is_same_v) + { + exported_slicedtube_class + + .def("inflate", (const SlicedTube& (SlicedTube::*)(const Vector&)) &SlicedTube::inflate, + CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_V_REF, + "rad"_a) + + .def("inflate", (const SlicedTube& (SlicedTube::*)(const SampledTraj&)) &SlicedTube::inflate, + CONST_SLICEDTUBE_T_REF_SLICEDTUBE_T_INFLATE_CONST_SAMPLEDTRAJ_V_REF, + "rad"_a) + ; + } + + exported_slicedtube_class + + .def(py::self == py::self, + BOOL_SLICEDTUBE_T_OPERATOREQ_CONST_SLICEDTUBE_REF_CONST, + "x"_a) + + .def(py::self != py::self, + "x"_a) + + .def("as_function", &SlicedTube::as_function, + ANALYTICFUNCTION_TYPENAME_EXPRTYPE_T_TYPE_SLICEDTUBE_T_AS_FUNCTION_CONST) + + .def("all_reals_value", &SlicedTube::all_reals_value, + T_SLICEDTUBE_T_ALL_REALS_VALUE_CONST) + + .def("empty_value", &SlicedTube::empty_value, + T_SLICEDTUBE_T_EMPTY_VALUE_CONST) + + .def("__repr__", [](const SlicedTube& x) { + std::ostringstream stream; + stream << x; + return string(stream.str()); + }, + OSTREAM_REF_OPERATOROUT_OSTREAM_REF_CONST_SLICEDTUBE_T_REF) + ; + + if constexpr(std::is_same_v || std::is_same_v) + { + exported_slicedtube_class + + .def("__call__", [](const SlicedTube& x, const Interval& t, const SlicedTube& v) + { + return x(t,v); + }, + T_SLICEDTUBE_T_OPERATORCALL_CONST_INTERVAL_REF_CONST_SLICEDTUBE_T_REF_CONST, + "t"_a, "v"_a) + + .def("integral", (T (SlicedTube::*)(const Interval&) const) &SlicedTube::integral, + T_SLICEDTUBE_T_INTEGRAL_CONST_INTERVAL_REF_CONST, + "t"_a) + + .def("integral", (T (SlicedTube::*)(const Interval&,const Interval&) const) &SlicedTube::integral, + T_SLICEDTUBE_T_INTEGRAL_CONST_INTERVAL_REF_CONST_INTERVAL_REF_CONST, + "t1"_a, "t2"_a) + + .def("partial_integral", (std::pair (SlicedTube::*)(const Interval&) const) &SlicedTube::partial_integral, + PAIR_TT_SLICEDTUBE_T_PARTIAL_INTEGRAL_CONST_INTERVAL_REF_CONST, + "t"_a) + + .def("partial_integral", (std::pair (SlicedTube::*)(const Interval&,const Interval&) const) &SlicedTube::partial_integral, + PAIR_TT_SLICEDTUBE_T_PARTIAL_INTEGRAL_CONST_INTERVAL_REF_CONST_INTERVAL_REF_CONST, + "t1"_a, "t2"_a) + + .def("primitive", (SlicedTube (SlicedTube::*)() const) &SlicedTube::primitive, + SLICEDTUBE_T_SLICEDTUBE_T_PRIMITIVE_CONST) + + .def("primitive", (SlicedTube (SlicedTube::*)(const T&) const) &SlicedTube::primitive, + SLICEDTUBE_T_SLICEDTUBE_T_PRIMITIVE_CONST_T_REF_CONST, + "x0"_a) + + .def("invert", (Interval (SlicedTube::*)(const T&) const) &SlicedTube::invert, + INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST, + "y"_a) + + .def("invert", (Interval (SlicedTube::*)(const T&,const Interval&) const) &SlicedTube::invert, + INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_INTERVAL_REF_CONST, + "y"_a, "t"_a) + + .def("invert", [](const SlicedTube& x, const T& y, py::list& v_t) + { + vector vector_t; + x.invert(y, vector_t); + v_t.clear(); + for(const auto& ti : vector_t) + v_t.append(ti); + }, + VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST, + "y"_a, "v_t"_a) + + .def("invert", [](const SlicedTube& x, const T& y, py::list& v_t, const Interval& t) + { + vector vector_t; + x.invert(y, vector_t, t); + v_t.clear(); + for(const auto& ti : vector_t) + v_t.append(ti); + }, + VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST_INTERVAL_REF_CONST, + "y"_a, "v_t"_a, "t"_a) + + .def("invert", (Interval (SlicedTube::*)(const T&,const SlicedTube&) const) &SlicedTube::invert, + INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_SLICEDTUBE_T_REF_CONST, + "y"_a, "v"_a) + + .def("invert", (Interval (SlicedTube::*)(const T&,const SlicedTube&,const Interval&) const) &SlicedTube::invert, + INTERVAL_SLICEDTUBE_T_INVERT_CONST_T_REF_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF_CONST, + "y"_a, "v"_a, "t"_a) + + .def("invert", [](const SlicedTube& x, const T& y, py::list& v_t, const SlicedTube& v) + { + vector vector_t; + x.invert(y, vector_t, v); + v_t.clear(); + for(const auto& ti : vector_t) + v_t.append(ti); + }, + VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST_SLICEDTUBE_T_REF_CONST, + "y"_a, "v_t"_a, "v"_a) + + .def("invert", [](const SlicedTube& x, const T& y, py::list& v_t, const SlicedTube& v, const Interval& t) + { + vector vector_t; + x.invert(y, vector_t, v, t); + v_t.clear(); + for(const auto& ti : vector_t) + v_t.append(ti); + }, + VOID_SLICEDTUBE_T_INVERT_CONST_T_REF_VECTOR_INTERVAL_REF_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF_CONST, + "y"_a, "v_t"_a, "v"_a, "t"_a) + + .def("mid", &SlicedTube::mid, + SAMPLEDTRAJ_TYPENAME_SCALAR_T_TYPE_SLICEDTUBE_T_MID_CONST); + ; + } + + if constexpr(std::is_same_v) + { + exported_slicedtube_class + + .def( + #if FOR_MATLAB + "get_item" + #else + "__getitem__" + #endif + , + [](const SlicedTube& x, Index_type i) -> SlicedTube + { + matlab::test_integer(i); + return x[matlab::input_index(i)]; + }, + SLICEDTUBE_INTERVAL_SLICEDTUBE_T_OPERATORCOMPO_INDEX_CONST, + "i"_a) + + .def("get_item_0", + [](const SlicedTube& x, Index_type i) -> SlicedTube + { + matlab::test_integer(i); + return x[i]; + }, + SLICEDTUBE_INTERVAL_SLICEDTUBE_T_OPERATORCOMPO_INDEX_CONST, + "i"_a) + + .def("subvector", + [](const SlicedTube& x, Index_type i, Index_type j) -> SlicedTube + { + matlab::test_integer(i,j); + return x.subvector(matlab::input_index(i),matlab::input_index(j)); + }, + SLICEDTUBE_INTERVALVECTOR_SLICEDTUBE_T_SUBVECTOR_INDEX_INDEX_CONST, + "i"_a, "j"_a) + + .def("subvector_0", + [](const SlicedTube& x, Index_type i, Index_type j) -> SlicedTube + { + matlab::test_integer(i,j); + return x.subvector(i,j); + }, + SLICEDTUBE_INTERVALVECTOR_SLICEDTUBE_T_SUBVECTOR_INDEX_INDEX_CONST, + "i"_a, "j"_a) + ; + } + + return exported_slicedtube_class; +} \ No newline at end of file diff --git a/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp b/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp new file mode 100644 index 000000000..b760e42da --- /dev/null +++ b/python/src/core/domains/tube/codac2_py_SlicedTube_operations.cpp @@ -0,0 +1,404 @@ +/** + * Codac binding (core) + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Simon Rohou + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include +#include "codac2_py_matlab.h" +#include "codac2_py_SlicedTube_operations_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py) + +using namespace codac2; +namespace py = pybind11; +using namespace pybind11::literals; + +namespace +{ + template + void bind_set_ops(py::class_,TubeBase>& pyclass) + { + if constexpr(!FOR_MATLAB) + { + pyclass + .def(py::self & py::self, + SLICEDTUBE_T_OPERATORINTER_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + .def(py::self &= py::self, + SLICEDTUBE_T_REF_OPERATORINTEREQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + .def(py::self | py::self, + SLICEDTUBE_T_OPERATORUNION_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + .def(py::self |= py::self, + SLICEDTUBE_T_REF_OPERATORUNIONEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()); + } + else + { + pyclass + .def("inter", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator&, + SLICEDTUBE_T_OPERATORINTER_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + .def("self_inter", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator&=, + SLICEDTUBE_T_REF_OPERATORINTEREQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + .def("union", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator|, + SLICEDTUBE_T_OPERATORUNION_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + .def("self_union", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator|=, + SLICEDTUBE_T_REF_OPERATORUNIONEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + } + + template + void bind_add_sub_ops(py::class_,TubeBase>& pyclass) + { + pyclass + .def("__pos__", + (const SlicedTube&(*)(const SlicedTube&)) &codac2::operator+, + CONST_SLICEDTUBE_T_REF_OPERATORPLUS_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__add__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator+, + SLICEDTUBE_T_OPERATORPLUS_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__add__", (SlicedTube(*)(const SlicedTube&,const T&)) &codac2::operator+, + SLICEDTUBE_T_OPERATORPLUS_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, + py::is_operator()) + + .def("__radd__", + [](const SlicedTube& x2, const T& x1) { return x1 + x2; }, + SLICEDTUBE_T_OPERATORPLUS_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__iadd__", (SlicedTube&(*)(SlicedTube&,const T&)) &codac2::operator+=, + SLICEDTUBE_T_REF_OPERATORPLUSEQ_SLICEDTUBE_T_REF_CONST_Q_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__iadd__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator+=, + SLICEDTUBE_T_REF_OPERATORPLUSEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__neg__", (SlicedTube(*)(const SlicedTube&)) &codac2::operator-, + SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__sub__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator-, + SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__sub__", (SlicedTube(*)(const SlicedTube&,const T&)) &codac2::operator-, + SLICEDTUBE_T_OPERATORMINUS_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, + py::is_operator()) + + .def("__rsub__", + [](const SlicedTube& x2, const T& x1) { return x1 - x2; }, + SLICEDTUBE_T_OPERATORMINUS_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__isub__", (SlicedTube&(*)(SlicedTube&,const T&)) &codac2::operator-=, + SLICEDTUBE_T_REF_OPERATORMINUSEQ_SLICEDTUBE_T_REF_CONST_Q_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__isub__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator-=, + SLICEDTUBE_T_REF_OPERATORMINUSEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + + template + void bind_mul_ops(py::class_,TubeBase>& pyclass) + { + pyclass + .def("__mul__", (SlicedTube(*)(const SlicedTube&,const Interval&)) &codac2::operator*, + SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF, + py::is_operator()) + + .def("__rmul__", + [](const SlicedTube& x2, const T& x1) { return x1 * x2; }, + SLICEDTUBE_T_OPERATORMUL_CONST_INTERVAL_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__imul__", (SlicedTube&(*)(SlicedTube&,const Interval&)) &codac2::operator*=, + SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_Q_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + + if constexpr(std::is_same_v) + { + pyclass + .def("__mul__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator*, + SLICEDTUBE_INTERVAL_OPERATORMUL_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::is_operator()) + + .def("__imul__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator*=, + SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + else + { + pyclass + .def("__mul__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator*, + std::is_same_v + ? SLICEDTUBE_INTERVALVECTOR_OPERATORMUL_CONST_SLICEDTUBE_INTERVALVECTOR_REF_CONST_SLICEDTUBE_INTERVALVECTOR_REF + : SLICEDTUBE_INTERVALMATRIX_OPERATORMUL_CONST_SLICEDTUBE_INTERVALMATRIX_REF_CONST_SLICEDTUBE_INTERVALMATRIX_REF, + py::is_operator()) + + .def("__mul__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator*, + SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::is_operator()) + + .def("__rmul__", + [](const SlicedTube& x2, const SlicedTube& x1) { return x1 * x2; }, + SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__mul__", (SlicedTube(*)(const SlicedTube&,const T&)) &codac2::operator*, + SLICEDTUBE_T_OPERATORMUL_CONST_SLICEDTUBE_T_REF_CONST_Q_REF, + py::is_operator()) + + .def("__rmul__", + [](const SlicedTube& x2, const T& x1) { return x1 * x2; }, + SLICEDTUBE_T_OPERATORMUL_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__imul__", (SlicedTube&(*)(SlicedTube&,const T&)) &codac2::operator*=, + SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_Q_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__imul__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator*=, + SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()) + + .def("__imul__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator*=, + SLICEDTUBE_T_REF_OPERATORMULEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + } + + template + void bind_div_ops(py::class_,TubeBase>& pyclass) + { + pyclass + .def("__truediv__", (SlicedTube(*)(const SlicedTube&,const Interval&)) &codac2::operator/, + SLICEDTUBE_T_OPERATORDIV_CONST_SLICEDTUBE_T_REF_CONST_INTERVAL_REF, + py::is_operator()) + + .def("__itruediv__", (SlicedTube&(*)(SlicedTube&,const Interval&)) &codac2::operator/=, + SLICEDTUBE_T_REF_OPERATORDIVEQ_SLICEDTUBE_T_REF_CONST_Q_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + + if constexpr(std::is_same_v) + { + pyclass + .def("__truediv__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator/, + SLICEDTUBE_INTERVAL_OPERATORDIV_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::is_operator()) + + .def("__rtruediv__", + [](const SlicedTube& x2, const Interval& x1) { return x1 / x2; }, + SLICEDTUBE_T_OPERATORDIV_CONST_Q_REF_CONST_SLICEDTUBE_T_REF, + py::is_operator()) + + .def("__itruediv__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator/=, + SLICEDTUBE_T_REF_OPERATORDIVEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_T_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + else + { + pyclass + .def("__truediv__", (SlicedTube(*)(const SlicedTube&,const SlicedTube&)) &codac2::operator/, + SLICEDTUBE_T_OPERATORDIV_CONST_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::is_operator()) + + .def("__itruediv__", (SlicedTube&(*)(SlicedTube&,const SlicedTube&)) &codac2::operator/=, + SLICEDTUBE_T_REF_OPERATORDIVEQ_SLICEDTUBE_T_REF_CONST_SLICEDTUBE_INTERVAL_REF, + py::return_value_policy::reference_internal, + py::is_operator()); + } + } + + void bind_scalar_slicedtube_functions(py::module& m) + { + m + .def("sqr", (SlicedTube (*)(const SlicedTube&)) &codac2::sqr, + SLICEDTUBE_INTERVAL_SQR_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("sqrt", (SlicedTube (*)(const SlicedTube&)) &codac2::sqrt, + SLICEDTUBE_INTERVAL_SQRT_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("pow", (SlicedTube (*)(const SlicedTube&,int)) &codac2::pow, + SLICEDTUBE_INTERVAL_POW_CONST_SLICEDTUBE_INTERVAL_REF_INT, + "x1"_a, "x2"_a, py::is_operator()) + + .def("pow", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::pow, + SLICEDTUBE_INTERVAL_POW_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("exp", (SlicedTube (*)(const SlicedTube&)) &codac2::exp, + SLICEDTUBE_INTERVAL_EXP_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("log", (SlicedTube (*)(const SlicedTube&)) &codac2::log, + SLICEDTUBE_INTERVAL_LOG_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("cos", (SlicedTube (*)(const SlicedTube&)) &codac2::cos, + SLICEDTUBE_INTERVAL_COS_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("sin", (SlicedTube (*)(const SlicedTube&)) &codac2::sin, + SLICEDTUBE_INTERVAL_SIN_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("tan", (SlicedTube (*)(const SlicedTube&)) &codac2::tan, + SLICEDTUBE_INTERVAL_TAN_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("acos", (SlicedTube (*)(const SlicedTube&)) &codac2::acos, + SLICEDTUBE_INTERVAL_ACOS_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("asin", (SlicedTube (*)(const SlicedTube&)) &codac2::asin, + SLICEDTUBE_INTERVAL_ASIN_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("atan", (SlicedTube (*)(const SlicedTube&)) &codac2::atan, + SLICEDTUBE_INTERVAL_ATAN_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("atan2", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::atan2, + SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("atan2", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::atan2, + SLICEDTUBE_INTERVAL_ATAN2_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("atan2", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::atan2, + SLICEDTUBE_INTERVAL_ATAN2_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("cosh", (SlicedTube (*)(const SlicedTube&)) &codac2::cosh, + SLICEDTUBE_INTERVAL_COSH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("sinh", (SlicedTube (*)(const SlicedTube&)) &codac2::sinh, + SLICEDTUBE_INTERVAL_SINH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("tanh", (SlicedTube (*)(const SlicedTube&)) &codac2::tanh, + SLICEDTUBE_INTERVAL_TANH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("acosh", (SlicedTube (*)(const SlicedTube&)) &codac2::acosh, + SLICEDTUBE_INTERVAL_ACOSH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("asinh", (SlicedTube (*)(const SlicedTube&)) &codac2::asinh, + SLICEDTUBE_INTERVAL_ASINH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("atanh", (SlicedTube (*)(const SlicedTube&)) &codac2::atanh, + SLICEDTUBE_INTERVAL_ATANH_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("abs", (SlicedTube (*)(const SlicedTube&)) &codac2::abs, + SLICEDTUBE_INTERVAL_ABS_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("min", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::min, + SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("min", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::min, + SLICEDTUBE_INTERVAL_MIN_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("min", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::min, + SLICEDTUBE_INTERVAL_MIN_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("max", (SlicedTube (*)(const SlicedTube&,const SlicedTube&)) &codac2::max, + SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("max", (SlicedTube (*)(const SlicedTube&,const Interval&)) &codac2::max, + SLICEDTUBE_INTERVAL_MAX_CONST_SLICEDTUBE_INTERVAL_REF_CONST_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("max", (SlicedTube (*)(const Interval&,const SlicedTube&)) &codac2::max, + SLICEDTUBE_INTERVAL_MAX_CONST_INTERVAL_REF_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, "x2"_a, py::is_operator()) + + .def("sign", (SlicedTube (*)(const SlicedTube&)) &codac2::sign, + SLICEDTUBE_INTERVAL_SIGN_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("integer", (SlicedTube (*)(const SlicedTube&)) &codac2::integer, + SLICEDTUBE_INTERVAL_INTEGER_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("floor", (SlicedTube (*)(const SlicedTube&)) &codac2::floor, + SLICEDTUBE_INTERVAL_FLOOR_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()) + + .def("ceil", (SlicedTube (*)(const SlicedTube&)) &codac2::ceil, + SLICEDTUBE_INTERVAL_CEIL_CONST_SLICEDTUBE_INTERVAL_REF, + "x1"_a, py::is_operator()); + } +} + +void export_SlicedTube_operations( + py::module& m, + py::class_,TubeBase>& py_SlicedTube_Interval, + py::class_,TubeBase>& py_SlicedTube_IntervalVector, + py::class_,TubeBase>& py_SlicedTube_IntervalMatrix) +{ + bind_set_ops(py_SlicedTube_Interval); + bind_set_ops(py_SlicedTube_IntervalVector); + bind_set_ops(py_SlicedTube_IntervalMatrix); + + bind_add_sub_ops(py_SlicedTube_Interval); + bind_add_sub_ops(py_SlicedTube_IntervalVector); + bind_add_sub_ops(py_SlicedTube_IntervalMatrix); + + bind_mul_ops(py_SlicedTube_Interval); + bind_mul_ops(py_SlicedTube_IntervalVector); + bind_mul_ops(py_SlicedTube_IntervalMatrix); + + bind_div_ops(py_SlicedTube_Interval); + bind_div_ops(py_SlicedTube_IntervalVector); + bind_div_ops(py_SlicedTube_IntervalMatrix); + + py_SlicedTube_IntervalMatrix + .def("__mul__", (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); +} diff --git a/python/src/core/domains/tube/codac2_py_TSlice.cpp b/python/src/core/domains/tube/codac2_py_TSlice.cpp index 6cb11ab83..405441cb9 100644 --- a/python/src/core/domains/tube/codac2_py_TSlice.cpp +++ b/python/src/core/domains/tube/codac2_py_TSlice.cpp @@ -32,12 +32,9 @@ void export_TSlice(py::module& m) .def("slices", &TSlice::slices, CONST_MAP_CONST_SLICEDTUBEBASE_PTRSHARED_PTR_SLICEBASE_REF_TSLICE_SLICES_CONST) - .def(py::self == py::self, - BOOL_TSLICE_OPERATOREQ_CONST_TSLICE_REF_CONST, - "x"_a) + .def(py::self == py::self, "x"_a) - .def(py::self != py::self, - "x"_a) + .def(py::self != py::self, "x"_a) ; } diff --git a/python/src/core/trajectory/codac2_py_AnalyticTraj.cpp b/python/src/core/trajectory/codac2_py_AnalyticTraj.cpp index 0a2f2b06e..6d5dce31d 100644 --- a/python/src/core/trajectory/codac2_py_AnalyticTraj.cpp +++ b/python/src/core/trajectory/codac2_py_AnalyticTraj.cpp @@ -32,9 +32,9 @@ void _export_AnalyticTraj(py::module& m, const string& class_name) exported_class - .def(py::init&,const Interval&>(), - ANALYTICTRAJ_T_ANALYTICTRAJ_CONST_ANALYTICFUNCTION_T_REF_CONST_INTERVAL_REF, - "f"_a, "tdomain"_a) + .def(py::init&>(), + ANALYTICTRAJ_T_ANALYTICTRAJ_CONST_INTERVAL_REF_CONST_ANALYTICFUNCTION_T_REF, + "tdomain"_a, "f"_a) .def("__call__", (typename T::Scalar (AnalyticTraj::*) (double) const) &AnalyticTraj::operator(), VIRTUAL_T_SCALAR_ANALYTICTRAJ_T_OPERATORCALL_DOUBLE_CONST, diff --git a/python/src/core/trajectory/codac2_py_TrajBase.h b/python/src/core/trajectory/codac2_py_TrajBase.h index d5a5e3951..7802cf6f5 100644 --- a/python/src/core/trajectory/codac2_py_TrajBase.h +++ b/python/src/core/trajectory/codac2_py_TrajBase.h @@ -70,7 +70,7 @@ void export_TrajBase(py::class_& pyclass) { return x.sampled(dt); }, - VIRTUAL_SAMPLEDTRAJ_T_TRAJBASE_T_SAMPLED_DOUBLE_CONST, + SAMPLEDTRAJ_T_TRAJBASE_T_SAMPLED_DOUBLE_CONST, "dt"_a) .def("sampled_as", [](const S& this_traj, const SampledTraj& x) diff --git a/python/src/graphics/figures/codac2_py_Figure2D.cpp b/python/src/graphics/figures/codac2_py_Figure2D.cpp index e94de5de1..6545cf26f 100644 --- a/python/src/graphics/figures/codac2_py_Figure2D.cpp +++ b/python/src/graphics/figures/codac2_py_Figure2D.cpp @@ -203,6 +203,10 @@ void export_Figure2D(py::module& m) VOID_FIGURE2D_PLOT_TRAJECTORY_CONST_SAMPLEDTRAJ_DOUBLE_REF_CONST_STYLEPROPERTIES_REF, "x"_a, "style"_a=StyleProperties()) + .def("plot_trajectory", (void(Figure2D::*)(const AnalyticTraj&,const StyleProperties&))&Figure2D::plot_trajectory, + VOID_FIGURE2D_PLOT_TRAJECTORY_CONST_ANALYTICTRAJ_SCALARTYPE_REF_CONST_STYLEPROPERTIES_REF, + "x"_a, "style"_a=StyleProperties()) + .def("plot_trajectories", (void(Figure2D::*)(const SampledTraj&))&Figure2D::plot_trajectories, VOID_FIGURE2D_PLOT_TRAJECTORIES_CONST_SAMPLEDTRAJ_VECTOR_REF, "x"_a) @@ -410,6 +414,10 @@ void export_Figure2D(py::module& m) STATIC_VOID_DEFAULTFIGURE_PLOT_TRAJECTORY_CONST_SAMPLEDTRAJ_DOUBLE_REF_CONST_STYLEPROPERTIES_REF, "x"_a, "style"_a=StyleProperties()) + .def_static("plot_trajectory", (void(*)(const AnalyticTraj&,const StyleProperties&))&DefaultFigure::plot_trajectory, + STATIC_VOID_DEFAULTFIGURE_PLOT_TRAJECTORY_CONST_ANALYTICTRAJ_SCALARTYPE_REF_CONST_STYLEPROPERTIES_REF + "x"_a, "style"_a=StyleProperties()) + .def_static("plot_trajectories", (void(*)(const SampledTraj&,const StyleProperties&))&DefaultFigure::plot_trajectories, STATIC_VOID_DEFAULTFIGURE_PLOT_TRAJECTORIES_CONST_SAMPLEDTRAJ_VECTOR_REF_CONST_STYLEPROPERTIES_REF, "x"_a, "style"_a=StyleProperties()) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4f6a00ed6..d06db4fd8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -61,6 +61,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/domains/codac2_BoolInterval.h ${CMAKE_CURRENT_SOURCE_DIR}/domains/codac2_Domain.h + ${CMAKE_CURRENT_SOURCE_DIR}/domains/codac2_Scalar.h ${CMAKE_CURRENT_SOURCE_DIR}/domains/codac2_Wrapper.h ${CMAKE_CURRENT_SOURCE_DIR}/domains/ellipsoid/codac2_Ellipsoid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/domains/ellipsoid/codac2_Ellipsoid_checks.cpp @@ -283,6 +284,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/trajectory/codac2_traj_cart_prod.h ${CMAKE_CURRENT_SOURCE_DIR}/trajectory/codac2_Traj_operator.h ${CMAKE_CURRENT_SOURCE_DIR}/trajectory/codac2_TrajBase.h + ${CMAKE_CURRENT_SOURCE_DIR}/trajectory/codac2_TrajBase_impl.h ) ################################################################################ diff --git a/src/core/domains/codac2_Scalar.h b/src/core/domains/codac2_Scalar.h new file mode 100644 index 000000000..ca848dd7e --- /dev/null +++ b/src/core/domains/codac2_Scalar.h @@ -0,0 +1,39 @@ +/** + * \file codac2_Scalar.h + * ---------------------------------------------------------------------------- + * \date 2026 + * \author Simon Rohou + * \copyright Copyright 2026 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +#include "codac2_Interval.h" +#include "codac2_Vector.h" +#include "codac2_Matrix.h" +#include "codac2_IntervalVector.h" +#include "codac2_IntervalMatrix.h" + +namespace codac2 +{ + template + struct Scalar + { }; + + template<> + struct Scalar { + using Type = double; + }; + + template + struct Scalar,OtherDerived>>::type> + { + using Type = Eigen::Matrix; + // Automatically sets: + // Scalar::Type = Vector + // Scalar::Type = Matrix + // + related possible expression templates + }; +} \ No newline at end of file diff --git a/src/core/domains/paving/codac2_Paving.h b/src/core/domains/paving/codac2_Paving.h index 7a8c2d4b9..3bd70a2a7 100644 --- a/src/core/domains/paving/codac2_Paving.h +++ b/src/core/domains/paving/codac2_Paving.h @@ -154,10 +154,11 @@ namespace codac2 { assert_release(x.size() == this->size()); + IntervalVector x_ = IntervalVector::empty(x.size()); + if(x.is_empty()) - return IntervalVector::empty(x.size()); + return x_; - IntervalVector x_ = IntervalVector::empty(x.size()); this->tree()->visit([&] (Node_ n) { diff --git a/src/core/domains/tube/codac2_Slice.h b/src/core/domains/tube/codac2_Slice.h index 71da4fae0..4d74f0389 100644 --- a/src/core/domains/tube/codac2_Slice.h +++ b/src/core/domains/tube/codac2_Slice.h @@ -17,6 +17,23 @@ namespace codac2 { + /** + * \class Slice + * \brief Codomain of a sliced tube over one temporal slice + * + * A ``Slice`` represents the codomain of a ``SlicedTube`` over one + * temporal interval of a ``TDomain``. + * + * This object is attached to one temporal slice ``TSlice`` and stores a + * codomain of type ``T``. The type ``T`` is typically ``Interval`` or + * ``IntervalVector``, or any custom domain implemented by the user. + * + * The inheritance from ``T`` is protected so that modifications of the + * codomain remain controlled by the ``Slice`` interface. Indeed, changing the + * codomain of a slice may require propagating contractions to adjacent gates. + * + * \tparam T codomain type + */ template class Slice : public SliceBase, protected T @@ -25,10 +42,26 @@ namespace codac2 { public: + /** + * \brief Creates a slice attached to a tube over a temporal slice + * + * \param tube sliced tube owning this slice + * \param it_tslice iterator to the associated temporal slice + * \param codomain codomain associated with this slice + */ explicit Slice(const SlicedTubeBase& tube, const std::list::iterator& it_tslice, const T& codomain) : SliceBase(tube, it_tslice), T(codomain) { } + /** + * \brief Creates a slice from another one for a given tube + * + * This constructor duplicates the codomain and keeps the same temporal + * support. + * + * \param s source slice + * \param tube sliced tube owning the copied slice + */ Slice(const Slice& s, const SlicedTubeBase& tube) : SliceBase(tube, s._it_tslice), T(s.codomain()) { } @@ -40,60 +73,124 @@ namespace codac2 Slice(Slice&&) = delete; Slice& operator=(Slice&&) = delete; + /** + * \brief Returns the sliced tube owning this slice + * + * \return reference to the parent sliced tube + */ inline const SlicedTube& tube() const { return static_cast&>(_tube); } - inline virtual std::shared_ptr copy() const + /** + * \brief Duplicates this slice + * + * \return shared pointer to a copy of this slice + */ + inline std::shared_ptr copy() const override { return std::make_shared(*this, this->_tube); } + /** + * \brief Returns the codomain dimension + * + * \return size of the codomain + */ inline Index size() const { return this->T::size(); } + /** + * \brief Returns a mutable reference to the codomain + * + * \note Prefer ``set()`` when adjacency propagation is required. + * + * \return mutable reference to the codomain + */ inline T& codomain() { - return (T&)(*this); + return static_cast(*this); } + /** + * \brief Returns a constant reference to the codomain + * + * \return constant reference to the codomain + */ inline const T& codomain() const { - return (const T&)(*this); + return static_cast(*this); } + /** + * \brief Tests whether this slice is a gate + * + * A slice is considered a gate when its temporal support is degenerate. + * + * \return ``true`` if this slice is a gate, ``false`` otherwise + */ inline bool is_gate() const { return t0_tf().is_degenerated(); } + /** + * \brief Returns the previous slice + * + * \return shared pointer to the previous slice, or ``nullptr`` if none exists + */ inline std::shared_ptr> prev_slice() const { return std::static_pointer_cast>( this->SliceBase::prev_slice()); } + /** + * \brief Returns the previous slice + * + * \return shared pointer to the previous slice, or ``nullptr`` if none exists + */ inline std::shared_ptr> prev_slice() { return std::const_pointer_cast>( static_cast&>(*this).prev_slice()); } + /** + * \brief Returns the next slice + * + * \return shared pointer to the next slice, or ``nullptr`` if none exists + */ inline std::shared_ptr> next_slice() const { return std::static_pointer_cast>( this->SliceBase::next_slice()); } + /** + * \brief Returns the next slice + * + * \return shared pointer to the next slice, or ``nullptr`` if none exists + */ inline std::shared_ptr> next_slice() { return std::const_pointer_cast>( static_cast&>(*this).next_slice()); } + /** + * \brief Returns the input gate of this slice + * + * If the previous temporal element is an explicit gate, its codomain is + * returned. Otherwise, the returned value is the intersection between this + * codomain and the codomain of the previous slice. If no previous slice + * exists, the codomain of this slice is returned. + * + * \return enclosure of the input gate + */ inline T input_gate() const { if(!prev_slice()) @@ -108,6 +205,16 @@ namespace codac2 } } + /** + * \brief Returns the output gate of this slice + * + * If the next temporal element is an explicit gate, its codomain is + * returned. Otherwise, the returned value is the intersection between this + * codomain and the codomain of the next slice. If no next slice exists, + * the codomain of this slice is returned. + * + * \return enclosure of the output gate + */ inline T output_gate() const { if(!next_slice()) @@ -122,6 +229,16 @@ namespace codac2 } } + /** + * \brief Returns enclosed lower and upper bounds over a temporal interval + * + * This method returns a pair whose first component encloses the reachable + * lower bounds and whose second component encloses the reachable upper + * bounds over ``t``. + * + * \param t temporal interval + * \return pair made of enclosed lower and upper bounds + */ inline std::pair enclosed_bounds(const Interval& t) const { T x = *this; x.set_empty(); @@ -156,6 +273,15 @@ namespace codac2 return bounds; } + /** + * \brief Sets the codomain of this slice + * + * If ``propagate`` is set to ``true``, adjacent gates are updated in order + * to preserve temporal consistency. + * + * \param x new codomain + * \param propagate if set to ``true``, propagates the update to adjacent gates + */ inline void set(const T& x, bool propagate = true) { assert_release(x.size() == this->size()); @@ -164,22 +290,49 @@ namespace codac2 update_adjacent_codomains(); } + /** + * \brief Initializes this codomain to its unbounded value + * + * No propagation is performed on adjacent slices. + */ inline void init() { this->T::init(); // Nothing to propagate to adjacent codomains } + /** + * \brief Sets this codomain to the empty set + * + * Adjacent gates are updated accordingly. + */ inline void set_empty() { set_empty(true); } + /** + * \brief Compares two slices + * + * The comparison only involves the codomain of the slices. + * + * \param x slice to compare with + * \return ``true`` if both codomains are equal, ``false`` otherwise + */ inline bool operator==(const Slice& x) const { return codomain() == x.codomain(); } + /** + * \brief Returns the polygonal enclosure associated with this scalar slice + * + * The enclosure is built from the codomain of this slice, its input/output + * gates and the derivative information provided by ``v``. + * + * \param v derivative slice + * \return polygonal enclosure of this slice + */ inline ConvexPolygon polygon_slice(const Slice& v) const requires std::is_same_v { @@ -200,6 +353,14 @@ namespace codac2 v); } + /** + * \brief Returns the polygonal enclosure associated with one component + * of a vector slice + * + * \param v derivative slice + * \param i component index + * \return polygonal enclosure of the \f$i\f$-th component + */ inline ConvexPolygon polygon_slice_i(const Slice& v, Index i) const requires std::is_same_v { @@ -375,14 +536,26 @@ namespace codac2 } } + /** + * \brief Stream output for a slice + * + * \param os output stream + * \param x slice to be displayed + * \return reference to the output stream + */ friend inline std::ostream& operator<<(std::ostream& os, const Slice& x) { os << x.t0_tf() - << "↦" << x.codomain() + << "->" << x.codomain() << std::flush; return os; } + /** + * \brief Returns the unbounded value associated with this codomain type + * + * \return unbounded value of type ``T`` + */ inline T all_reals_value() const { T x = codomain(); @@ -390,6 +563,11 @@ namespace codac2 return x; } + /** + * \brief Returns the empty value associated with this codomain type + * + * \return empty value of type ``T`` + */ inline T empty_value() const { T x = codomain(); @@ -403,6 +581,11 @@ namespace codac2 template friend class SlicedTube; + /** + * \brief Sets this codomain to the empty set + * + * \param propagate if set to ``true``, propagates the update to adjacent gates + */ inline void set_empty(bool propagate) { this->T::set_empty(); @@ -410,6 +593,12 @@ namespace codac2 update_adjacent_codomains(); } + /** + * \brief Propagates codomain contractions to adjacent gates + * + * This method preserves consistency between a slice and its neighboring + * gates when one of them is contracted. + */ inline void update_adjacent_codomains() { if(prev_slice()) diff --git a/src/core/domains/tube/codac2_SliceBase.h b/src/core/domains/tube/codac2_SliceBase.h index 567f33140..5afcdad1c 100644 --- a/src/core/domains/tube/codac2_SliceBase.h +++ b/src/core/domains/tube/codac2_SliceBase.h @@ -18,26 +18,100 @@ namespace codac2 class TSlice; class SlicedTubeBase; + /** + * \class SliceBase + * \brief Base class for codomain slices attached to a temporal slice + * + * A ``SliceBase`` represents the codomain of a sliced tube over one temporal + * slice of a ``TDomain``. + * + * This abstract class stores: + * - a reference to the parent ``SlicedTubeBase``, + * - an iterator to the corresponding ``TSlice`` in the temporal partition. + * + * Concrete derived classes, such as ``Slice``, provide the actual codomain + * representation and the associated operations. + */ class SliceBase { public: - SliceBase(const SlicedTubeBase& tube, const std::list::iterator& it_tslice); + /** + * \brief Virtual destructor + */ + virtual ~SliceBase() = default; + /** + * \brief Duplicates this slice + * + * \return shared pointer to a copy of this slice + */ virtual std::shared_ptr copy() const = 0; + + /** + * \brief Initializes this slice to its unbounded codomain + */ virtual void init() = 0; + + /** + * \brief Sets this slice to the empty codomain + */ virtual void set_empty() = 0; + /** + * \brief Returns the temporal domain of this slice + * + * \return temporal interval associated with this slice + */ const Interval& t0_tf() const; + + /** + * \brief Returns the temporal slice associated with this object + * + * \return reference to the underlying ``TSlice`` + */ const TSlice& tslice() const; + /** + * \brief Returns the previous slice of the same tube + * + * The previous slice is searched in the temporal element preceding the + * current ``TSlice``. + * + * \return shared pointer to the previous slice, or ``nullptr`` if this + * slice is the first one + */ std::shared_ptr prev_slice() const; - std::shared_ptr next_slice() const; + /** + * \brief Returns the next slice of the same tube + * + * The next slice is searched in the temporal element following the current + * ``TSlice``. + * + * \return shared pointer to the next slice, or ``nullptr`` if this slice + * is the last one + */ + std::shared_ptr next_slice() const; protected: + + /** + * \brief Creates a slice attached to a tube and a temporal slice + * + * \param tube sliced tube owning this slice + * \param it_tslice iterator to the associated temporal slice + */ + SliceBase(const SlicedTubeBase& tube, const std::list::iterator& it_tslice); + /** + * \brief Parent sliced tube + */ const SlicedTubeBase& _tube; + + /** + * \brief Iterator to the associated temporal slice + */ std::list::iterator _it_tslice; friend class TDomain; diff --git a/src/core/domains/tube/codac2_SlicedTube.h b/src/core/domains/tube/codac2_SlicedTube.h index 6d9f7fd80..f8dca4c91 100644 --- a/src/core/domains/tube/codac2_SlicedTube.h +++ b/src/core/domains/tube/codac2_SlicedTube.h @@ -13,14 +13,37 @@ #include "codac2_AnalyticFunction.h" #include "codac2_Tube_operator.h" #include "codac2_CtcDeriv.h" +#include "codac2_Scalar.h" +#include "codac2_AnalyticTraj.h" namespace codac2 { + /** + * \class SlicedTube + * \brief Tube represented over a sliced temporal domain + * + * A ``SlicedTube`` is a tube whose temporal domain is represented by a + * shared ``TDomain`` made of ``TSlice`` objects. + * + * For each temporal slice of this partition, the tube stores a ``Slice`` + * describing the codomain of the tube over that temporal support. + * + * The codomain type ``T`` is typically ``Interval`` or ``IntervalVector``, + * or any custom domain implemented by the user. + * + * \tparam T codomain type of the tube + */ template class SlicedTube : public SlicedTubeBase { public: + /** + * \brief Creates a sliced tube with constant codomain over all temporal slices + * + * \param tdomain shared temporal domain of the tube + * \param codomain codomain assigned to each slice + */ explicit SlicedTube(const std::shared_ptr& tdomain, const T& codomain) : SlicedTubeBase(tdomain) @@ -32,6 +55,15 @@ namespace codac2 }); } + /** + * \brief Creates a sliced tube by evaluating an analytic function on each temporal slice + * + * The function is evaluated on each temporal interval of the associated + * ``TDomain``. + * + * \param tdomain shared temporal domain of the tube + * \param f analytic function of one scalar variable + */ explicit SlicedTube(const std::shared_ptr& tdomain, const AnalyticFunction::Type>& f) : SlicedTubeBase(tdomain) @@ -46,19 +78,55 @@ namespace codac2 }); } + /** + * \brief Creates a sliced tube by evaluating an analytic trajectory on each temporal slice + * + * The function is evaluated on each temporal interval of the associated + * ``TDomain``. + * + * \param tdomain shared temporal domain of the tube + * \param x analytic trajectory + */ + template + requires std::is_same_v::Type,V> + explicit SlicedTube(const std::shared_ptr& tdomain, + const AnalyticTraj& x) + : SlicedTubeBase(tdomain) + { + for(auto it = _tdomain->begin(); it != _tdomain->end(); ++it) + it->_slices.insert({ + this, + std::make_shared>(*this, it, x.eval((Interval)*it)) + }); + } + + /** + * \brief Creates a sliced tube from a sampled trajectory + * + * \tparam V sampled value type + * \param tdomain shared temporal domain of the tube + * \param x sampled trajectory evaluated on each temporal slice + */ template requires std::is_same_v::Domain,T> explicit SlicedTube(const std::shared_ptr& tdomain, - const SampledTraj& f) + const SampledTraj& x) : SlicedTubeBase(tdomain) { for(auto it = _tdomain->begin(); it != _tdomain->end(); ++it) it->_slices.insert({ this, - std::make_shared>(*this, it, f((Interval)*it)) + std::make_shared>(*this, it, x((Interval)*it)) }); } + /** + * \brief Copy constructor + * + * The copied tube shares the same ``TDomain`` as the source tube. + * + * \param x tube to be copied + */ SlicedTube(const SlicedTube& x) : SlicedTubeBase(x.tdomain()) { @@ -69,6 +137,14 @@ namespace codac2 }); } + /** + * \brief Assignment operator + * + * Both tubes are expected to share the same temporal domain object. + * + * \param x source tube + * \return reference to this tube + */ inline SlicedTube& operator=(const SlicedTube& x) { assert_release(_tdomain == x._tdomain); @@ -79,11 +155,23 @@ namespace codac2 return *this; } + /** + * \brief Returns the codomain dimension + * + * \return dimension of the codomain type ``T`` + */ inline Index size() const { return first_slice()->size(); } + /** + * \brief Returns the matrix shape of this tube codomain + * + * For scalar codomains, the returned shape is ``{1,1}``. + * + * \return pair ``(rows, cols)`` + */ virtual std::pair shape() const { if constexpr(std::is_same_v::Type,ScalarType>) @@ -92,6 +180,13 @@ namespace codac2 return {first_slice()->codomain().rows(),first_slice()->codomain().cols()}; } + /** + * \brief Returns the volume of this tube + * + * Only non-gate slices contribute to the volume. + * + * \return sum of the volumes of all non-gate slices + */ inline double volume() const { double volume = 0.; @@ -101,66 +196,125 @@ namespace codac2 return volume; } + /** + * \brief Returns the first slice of this tube + * + * \return shared pointer to the first slice + */ inline std::shared_ptr> first_slice() { return std::const_pointer_cast>( static_cast(*this).first_slice()); } + /** + * \brief Returns the first slice of this tube + * + * \return shared pointer to the first slice + */ inline std::shared_ptr> first_slice() const { return std::static_pointer_cast>( this->SlicedTubeBase::first_slice()); } + /** + * \brief Returns the last slice of this tube + * + * \return shared pointer to the last slice + */ inline std::shared_ptr> last_slice() { return std::const_pointer_cast>( static_cast(*this).last_slice()); } + /** + * \brief Returns the last slice of this tube + * + * \return shared pointer to the last slice + */ inline std::shared_ptr> last_slice() const { return std::static_pointer_cast>( this->SlicedTubeBase::last_slice()); } + /** + * \brief Returns the slice attached to a temporal iterator + * + * \param it iterator to a ``TSlice`` of the shared ``TDomain`` + * \return shared pointer to the corresponding slice + */ inline std::shared_ptr> slice(const std::list::iterator& it) { return std::const_pointer_cast>( static_cast(*this).slice(it)); } + /** + * \brief Returns the slice attached to a temporal iterator + * + * \param it constant iterator to a ``TSlice`` of the shared ``TDomain`` + * \return shared pointer to the corresponding slice + */ inline std::shared_ptr> slice(const std::list::const_iterator& it) const { return std::static_pointer_cast>( it->slices().at(this)); } + /** + * \brief Returns the slice attached to a reverse temporal iterator + * + * \param it reverse iterator to a ``TSlice`` of the shared ``TDomain`` + * \return shared pointer to the corresponding slice + */ inline std::shared_ptr> slice(const std::list::reverse_iterator& it) { return std::const_pointer_cast>( static_cast(*this).slice(it)); } + /** + * \brief Returns the slice attached to a reverse temporal iterator + * + * \param it constant reverse iterator to a ``TSlice`` of the shared ``TDomain`` + * \return shared pointer to the corresponding slice + */ inline std::shared_ptr> slice(const std::list::const_reverse_iterator& it) const { return std::static_pointer_cast>( it->slices().at(this)); } + /** + * \brief Returns the slice attached to a temporal slice pointer + * + * This overload is mainly intended for language bindings. + * + * \param ptr pointer to a temporal slice of the shared ``TDomain`` + * \return shared pointer to the corresponding slice + */ inline std::shared_ptr> slice(std::shared_ptr ptr) { // Used in Python binding auto it = std::find_if(_tdomain->begin(), _tdomain->end(), [&](TSlice& t){ return &t == ptr.get(); }); + assert(it != _tdomain->end()); return slice(it); } + /** + * \brief Tests whether this tube is empty + * + * A fast evaluation is done by considering gates first, + * then envelopes, which allows to quickly identify an empty set + * + * \return ``true`` if at least one slice is empty, ``false`` otherwise + */ inline bool is_empty() const { - // Fast evaluation by considering gates first, then envelopes, - // which allows to quickly identify an empty set for(const auto& s : *this) if(s.is_gate() && s.is_empty()) return true; @@ -170,6 +324,11 @@ namespace codac2 return false; } + /** + * \brief Tests whether this tube is unbounded + * + * \return ``true`` if at least one slice is unbounded, ``false`` otherwise + */ inline bool is_unbounded() const { for(const auto& s : *this) @@ -178,6 +337,13 @@ namespace codac2 return false; } + /** + * \brief Returns the global codomain of this tube + * + * The returned codomain is the union of the codomains of all slices. + * + * \return global codomain enclosure + */ inline T codomain() const { T x = first_slice()->codomain(); @@ -206,6 +372,12 @@ namespace codac2 return codomain; } + /** + * \brief Evaluates this tube over a temporal interval + * + * \param t temporal interval + * \return enclosure of \f$[x]([t])\f$ + */ T operator()(const Interval& t) const { return eval_common(t, @@ -214,6 +386,13 @@ namespace codac2 }); } + /** + * \brief Returns the optimal evaluation over a temporal interval using a derivative tube + * + * \param t temporal interval + * \param v derivative tube such that \f$\dot{x}(\cdot)\in[v](\cdot)\f$ + * \return enclosure of \f$[x]([t])\f$ + */ T operator()(const Interval& t, const SlicedTube& v) const requires (std::is_same_v || std::is_same_v) { @@ -223,6 +402,12 @@ namespace codac2 }); } + /** + * \brief Returns enclosed lower and upper bounds over a temporal interval + * + * \param t temporal interval + * \return pair made of enclosed lower and upper bounds + */ std::pair enclosed_bounds(const Interval& t) const { auto x = this->empty_value(); @@ -250,6 +435,11 @@ namespace codac2 return bounds; } + /** + * \brief Sets all codomains of this tube to the same value + * + * \param codomain new codomain + */ inline void set(const T& codomain) { assert_release(codomain.size() == this->size()); @@ -263,12 +453,29 @@ namespace codac2 s.set(codomain, false); } + /** + * \brief Sets the codomain at one temporal instant + * + * The temporal domain may be refined so that ``t`` becomes an explicit gate. + * + * \param codomain new codomain + * \param t temporal instant + */ inline void set(const T& codomain, double t) { assert_release(codomain.size() == this->size()); slice(_tdomain->sample(t,true))->set(codomain); } + /** + * \brief Sets the codomain over a temporal interval + * + * The temporal domain may be refined so that the bounds of ``t`` become + * explicit gates. + * + * \param codomain new codomain + * \param t temporal interval + */ inline void set(const T& codomain, const Interval& t) { auto it_lb = _tdomain->sample(t.lb(), t.is_degenerated()); @@ -294,6 +501,12 @@ namespace codac2 } while(it_lb != it_ub && (++it_lb) != _tdomain->end()); } + /** + * \brief Sets the codomain of the \f$i\f$-th stored slice + * + * \param codomain new codomain + * \param i slice index + */ inline void set_ith_slice(const T& codomain, Index i) { Index j = 0; @@ -305,6 +518,13 @@ namespace codac2 } } + /** + * \brief Inflates this tube by a constant radius + * + * \tparam V radius type + * \param rad inflation radius + * \return reference to this tube + */ template requires (std::is_same_v::Domain,T> || std::is_same_v) const SlicedTube& inflate(const V& rad) @@ -321,6 +541,13 @@ namespace codac2 return *this; } + /** + * \brief Inflates this tube by a time-varying sampled radius + * + * \tparam V sampled radius type + * \param rad sampled inflation radius + * \return reference to this tube + */ template requires (std::is_same_v::Domain,T> || std::is_same_v) const SlicedTube& inflate(const SampledTraj& rad) @@ -339,6 +566,12 @@ namespace codac2 return *this; } + /** + * \brief Extracts one scalar component of this tube + * + * \param i component index + * \return scalar sliced tube corresponding to the \f$i\f$-th component + */ SlicedTube operator[](Index i) const { assert_release(i >= 0 && i < size()); @@ -348,6 +581,13 @@ namespace codac2 return xi; } + /** + * \brief Extracts a subvector of this tube + * + * \param i first component index + * \param j last component index + * \return sliced tube associated with the subvector \f$[i,j]\f$ + */ SlicedTube subvector(Index i, Index j) const { assert_release(i >= 0 && i <= j && j < size()); @@ -357,6 +597,15 @@ namespace codac2 return xij; } + /** + * \brief Compares two sliced tubes + * + * Two sliced tubes are equal if they share the same temporal partition and + * if all corresponding codomains are equal. + * + * \param x tube to compare with + * \return ``true`` if both tubes are equal, ``false`` otherwise + */ inline bool operator==(const SlicedTube& x) const { if(!TDomain::are_same(tdomain(), x.tdomain())) @@ -372,7 +621,15 @@ namespace codac2 return true; } - inline SlicedTube operator&=(const SlicedTube& x) + /** + * \brief Intersects this tube with another one + * + * Both tubes are expected to be defined over the same temporal partition. + * + * \param x tube to intersect with + * \return updated tube + */ + inline SlicedTube& operator&=(const SlicedTube& x) { assert(TDomain::are_same(tdomain(), x.tdomain())); auto it_this = _tdomain->begin(); @@ -389,16 +646,29 @@ namespace codac2 return *this; } + /** + * \brief Stream output for a sliced tube + * + * \param os output stream + * \param x sliced tube to display + * \return reference to the output stream + */ friend inline std::ostream& operator<<(std::ostream& os, const SlicedTube& x) { os << x.t0_tf() - << "↦" << (x.is_empty() ? x.empty_value() : x.codomain()) + << "->" << (x.is_empty() ? x.empty_value() : x.codomain()) << ", " << x.nb_slices() << " slice" << (x.nb_slices() > 1 ? "s" : "") << std::flush; return os; } + /** + * \brief Returns an analytic wrapper of this tube, allowing to + * evaluate this tube as an operator in an analytic expression. + * + * \return analytic function associated with this tube + */ AnalyticFunction::Type> as_function() const { ScalarVar t; @@ -409,6 +679,12 @@ namespace codac2 }; } + // Inversion related methods + + /** + * \defgroup codac2_slicedtube_inversion Inversion of sliced tubes + */ + template Interval invert_common(const T& y, const Interval& t, const Func& apply_invert) const { @@ -481,15 +757,29 @@ namespace codac2 } /** - * \brief Returns the interval inversion \f$[x]^{-1}([y])\f$ + * \brief Returns the interval inversion \f$\left[[x]^{-1}([y])\right]\f$. + * + * If the inversion results in several pre-images, their union is returned. + * + * \param y interval codomain + * \return the hull of \f$[x]^{-1}([y])\f$ + */ + Interval invert(const T& y) const + { + return invert(y,t0_tf()); + } + + /** + * \ingroup codac2_slicedtube_inversion + * \brief Returns the interval inversion \f$\left[[x]^{-1}([y])\right]\f$. * - * \note If the inversion results in several pre-images, their union is returned + * If the inversion results in several pre-images, their union is returned. * * \param y interval codomain - * \param t optional temporal domain on which the inversion will be performed + * \param t (optional) temporal domain on which the inversion will be performed * \return the hull of \f$[x]^{-1}([y])\f$ */ - Interval invert(const T& y, const Interval& t = Interval()) const + Interval invert(const T& y, const Interval& t) const { return invert_common(y, t, [this,&y](auto it, const Interval& t_) { @@ -498,14 +788,27 @@ namespace codac2 } /** - * \brief Computes the set of continuous values of the inversion \f$[x]^{-1}([y])\f$ + * \brief Computes the set of continuous values of the inversion \f$[x]^{-1}([y])\f$. + * + * \param y interval codomain + * \param v_t vector of the sub-tdomains \f$[t_k]\f$ for which + * \f$\forall t\in[t_k] \mid x(t)\in[y], x(\cdot)\in[x](\cdot)\f$ + */ + void invert(const T& y, std::vector &v_t) const + { + invert(y,v_t,t0_tf()); + } + + /** + * \ingroup codac2_slicedtube_inversion + * \brief Computes the set of continuous values of the inversion \f$[x]^{-1}([y])\f$. * * \param y interval codomain * \param v_t vector of the sub-tdomains \f$[t_k]\f$ for which * \f$\forall t\in[t_k] \mid x(t)\in[y], x(\cdot)\in[x](\cdot)\f$ - * \param t optional temporal domain on which the inversion will be performed + * \param t (optional) temporal domain on which the inversion will be performed */ - void invert(const T& y, std::vector &v_t, const Interval& t = Interval()) const + void invert(const T& y, std::vector &v_t, const Interval& t) const { return invert_common_subsets(y, v_t, t, [this,&y](auto it, const Interval& t_) { @@ -514,17 +817,34 @@ namespace codac2 } /** - * \brief Returns the optimal interval inversion \f$[x]^{-1}([y])\f$ + * \brief Returns the optimal interval inversion \f$\left[[x]^{-1}([y])\right]\f$. + * + * The knowledge of the derivative tube \f$[v](\cdot)\f$ allows a finer inversion. + * If the inversion results in several pre-images, their union is returned. + * + * \param y interval codomain + * \param v derivative tube such that \f$\dot{x}(\cdot)\in[v](\cdot)\f$ + * \return hull of \f$[x]^{-1}([y])\f$ + */ + Interval invert(const T& y, const SlicedTube& v) const + requires (std::is_same_v || std::is_same_v) + { + return invert(y,v,t0_tf()); + } + + /** + * \ingroup codac2_slicedtube_inversion + * \brief Returns the optimal interval inversion \f$\left[[x]^{-1}([y])\right]\f$. * - * \note The knowledge of the derivative tube \f$[v](\cdot)\f$ allows a finer inversion - * \note If the inversion results in several pre-images, their union is returned + * The knowledge of the derivative tube \f$[v](\cdot)\f$ allows a finer inversion. + * If the inversion results in several pre-images, their union is returned. * * \param y interval codomain * \param v derivative tube such that \f$\dot{x}(\cdot)\in[v](\cdot)\f$ - * \param t optional temporal domain on which the inversion will be performed + * \param t (optional) temporal domain on which the inversion will be performed * \return hull of \f$[x]^{-1}([y])\f$ */ - Interval invert(const T& y, const SlicedTube& v, const Interval& t = Interval()) const + Interval invert(const T& y, const SlicedTube& v, const Interval& t) const requires (std::is_same_v || std::is_same_v) { return invert_common(y, t, @@ -534,17 +854,34 @@ namespace codac2 } /** - * \brief Computes the set of continuous values of the optimal inversion \f$[x]^{-1}([y])\f$ + * \brief Computes the set of continuous values of the optimal inversion \f$[x]^{-1}([y])\f$. + * + * The knowledge of the derivative tube \f$[v](\cdot)\f$ allows finer inversions. + * + * \param y interval codomain + * \param v_t vector of the sub-tdomains \f$[t_k]\f$ for which + * \f$\forall t\in[t_k] \mid x(t)\in[y], x(\cdot)\in[x](\cdot), \dot{x}(\cdot)\in[v](\cdot)\f$ + * \param v derivative tube such that \f$\dot{x}(\cdot)\in[v](\cdot)\f$ + */ + void invert(const T& y, std::vector &v_t, const SlicedTube& v) const + requires (std::is_same_v || std::is_same_v) + { + invert(y,v_t,v,t0_tf()); + } + + /** + * \ingroup codac2_slicedtube_inversion + * \brief Computes the set of continuous values of the optimal inversion \f$[x]^{-1}([y])\f$. * - * \note The knowledge of the derivative tube \f$[v](\cdot)\f$ allows finer inversions + * The knowledge of the derivative tube \f$[v](\cdot)\f$ allows finer inversions. * * \param y interval codomain * \param v_t vector of the sub-tdomains \f$[t_k]\f$ for which - * \f$\exists t\in[t_k] \mid x(t)\in[y], x(\cdot)\in[x](\cdot), \dot{x}(\cdot)\in[v](\cdot)\f$ + * \f$\forall t\in[t_k] \mid x(t)\in[y], x(\cdot)\in[x](\cdot), \dot{x}(\cdot)\in[v](\cdot)\f$ * \param v derivative tube such that \f$\dot{x}(\cdot)\in[v](\cdot)\f$ - * \param t optional temporal domain on which the inversion will be performed + * \param t (optional) temporal domain on which the inversion will be performed */ - void invert(const T& y, std::vector &v_t, const SlicedTube& v, const Interval& t = Interval()) const + void invert(const T& y, std::vector &v_t, const SlicedTube& v, const Interval& t) const requires (std::is_same_v || std::is_same_v) { return invert_common_subsets(y, v_t, t, @@ -555,11 +892,119 @@ namespace codac2 // Integral related methods + /** + * \defgroup codac2_slicedtube_integrals Integration and primitive operations on sliced tubes + * \brief The following methods are valid for tubes defined for ``Interval`` or + * ``IntervalVector`` codomains. The returned values are integral enclosures of + * same type (respectively, ``Interval`` or ``IntervalVector``). + */ + + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns an enclosure of the integrals of this tube from \f$t_0\f$ to \f$[t]\f$. + * + * This method computes an enclosure of + * \f[ + * \left\{ \int_{t_0}^{\tau} [x](s)\,ds \;\middle|\; \tau\in[t] \right\}. + * \f] + * + * It is obtained from ``partial_integral(t)`` by taking the hull between + * the lower bound of the lower enclosure and the upper bound of the upper + * enclosure. + * + * \param t temporal interval \f$[t]\f$ + * \return enclosure of the integrals of this tube over ``t`` + */ T integral(const Interval& t) const; + + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns an enclosure of the integrals of this tube between the + * time intervals\f$[t_1]\f$ and \f$[t_2]\f$. + * + * This method computes an enclosure of + * \f[ + * \left\{ \int_{\tau_1}^{\tau_2} [x](s)\,ds + * \;\middle|\; \tau_1\in[t_1],\ \tau_2\in[t_2] \right\}. + * \f] + * + * The result is obtained by subtracting the partial integral enclosures at + * ``t1`` and ``t2``. + * + * \param t1 first temporal interval \f$[t_1]\f$ + * \param t2 second temporal interval \f$[t_2]\f$ + * \return enclosure of the integrals of this tube between ``t1`` and ``t2`` + */ T integral(const Interval& t1, const Interval& t2) const; + + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns lower and upper enclosures of the integrals of this tube + * \f$[x](\cdot)=[x^-(\cdot),x^+(\cdot)]\f$ from \f$t_0\f$ to \f$[t]\f$. + * + * This method returns a pair \f$([p^-],[p^+])\f$ such that: + * \f[ + * [p^-]\supset + * \left\{ \int_{t_0}^{\tau} x^-(s)\,ds + * \;\middle|\; \tau\in[t] + * \right\} + * \mathrm{~~and~~} + * [p^+]\supset + * \left\{\int_{t_0}^{\tau} x^+(s)\,ds + * \;\middle|\; \tau\in[t] + * \right\}. + * \f] + * + * This representation preserves more information than ``integral(t)``, + * which only returns the hull of these partial integral bounds. + * + * \param t temporal interval \f$[t]\f$ + * \return pair of lower and upper partial integral enclosures over ``t`` + */ std::pair partial_integral(const Interval& t) const; + + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns lower and upper enclosures of the integrals of this tube + * \f$[x](\cdot)=[x^-(\cdot),x^+(\cdot)]\f$ between \f$[t_1]\f$ and \f$[t_2]\f$. + * + * This method returns a pair obtained by subtracting the partial integral + * enclosures at ``t1`` from those at ``t2``. The returned pair \f$([p^-],[p^+])\f$ + * is such that: + * \f[ + * [p^-]\supset + * \left\{ \int_{\tau_1}^{\tau_2} x^-(s)\,ds + * \;\middle|\; \tau_1\in[t_1],\ \tau_2\in[t_2] + * \right\}, + * \f] + * and + * \f[ + * [p^+]\supset + * \left\{\int_{\tau_1}^{\tau_2} x^+(s)\,ds + * \;\middle|\; \tau_1\in[t_1],\ \tau_2\in[t_2] + * \right\}. + * \f] + * + * \param t1 first temporal interval \f$[t_1]\f$ + * \param t2 second temporal interval \f$[t_2]\f$ + * \return pair of lower and upper enclosures of the integrals + */ std::pair partial_integral(const Interval& t1, const Interval& t2) const; + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns a primitive of this tube with zero initial condition. + * + * This is a shorthand for ``primitive(x0)`` with \f$x_0 = 0\f$. + * + * In other words, the returned tube encloses solutions of + * \f[ + * \dot{p}(\cdot) \in [x](\cdot), + * \qquad p(t_0)=0. + * \f] + * + * \return primitive tube with zero initial condition + */ inline SlicedTube primitive() const { auto x0 = all_reals_value(); @@ -567,6 +1012,27 @@ namespace codac2 return primitive(x0); } + /** + * \ingroup codac2_slicedtube_integrals + * \brief Returns a primitive of this tube with prescribed initial condition + * + * This method constructs a tube ``p`` on the same temporal domain as this + * tube, imposes the initial condition \f$p(t_0)=x_0\f$, and contracts + * ``p`` with this tube through the derivative relation using ``CtcDeriv``. + * + * In other words, the returned tube encloses solutions of + * \f[ + * \dot{p}(\cdot) \in [x](\cdot), + * \qquad p(t_0)=x_0. + * \f] + * + * \note The current implementation may create an explicit gate at the + * initial time \f$t_0\f$ if it is not already present in the + * temporal partition. + * + * \param x0 initial condition at \f$t_0\f$ + * \return primitive tube satisfying the prescribed initial condition + */ inline SlicedTube primitive(const T& x0) const { auto x = all_reals_value(); @@ -577,6 +1043,11 @@ namespace codac2 return p; } + /** + * \brief Returns the unbounded codomain value associated with ``T`` + * + * \return unbounded value of type ``T`` + */ inline T all_reals_value() const { T x = first_slice()->codomain(); @@ -584,6 +1055,11 @@ namespace codac2 return x; } + /** + * \brief Returns the empty codomain value associated with ``T`` + * + * \return empty value of type ``T`` + */ inline T empty_value() const { T x = first_slice()->codomain(); @@ -591,11 +1067,15 @@ namespace codac2 return x; } - template - // requires std::is_same_v::Domain,T> - inline auto mid() const + /** + * \brief Returns the approximated midpoint trajectory of this tube + * + * \tparam V sampled value type + * \return midpoint sampled trajectory + */ + inline SampledTraj::Type> mid() const { - SampledTraj m; + SampledTraj::Type> m; double t0 = _tdomain->t0_tf().lb(); m.set((*this)(t0).mid(), t0); for(const auto& s : *this) @@ -609,6 +1089,9 @@ namespace codac2 using base_container = std::list; + /** + * \brief Mutable iterator over the slices of this tube + */ struct iterator : public base_container::iterator { public: @@ -631,9 +1114,19 @@ namespace codac2 SlicedTube& _x; }; + /** + * \brief Returns an iterator to the first temporal slice + */ iterator begin() { return { *this, _tdomain->begin() }; } + + /** + * \brief Returns an iterator past the last temporal slice + */ iterator end() { return { *this, _tdomain->end() }; } + /** + * \brief Mutable reverse iterator over the slices of this tube + */ struct reverse_iterator : public base_container::reverse_iterator { public: @@ -656,9 +1149,19 @@ namespace codac2 SlicedTube& _x; }; + /** + * \brief Returns a reverse iterator to the last temporal slice + */ reverse_iterator rbegin() { return { *this, _tdomain->rbegin() }; } + + /** + * \brief Returns a reverse iterator past the first temporal slice + */ reverse_iterator rend() { return { *this, _tdomain->rend() }; } + /** + * \brief Constant iterator over the slices of this tube + */ struct const_iterator : public base_container::const_iterator { public: @@ -681,9 +1184,19 @@ namespace codac2 const SlicedTube& _x; }; + /** + * \brief Returns a constant iterator to the first temporal slice + */ const_iterator begin() const { return { *this, _tdomain->cbegin() }; } + + /** + * \brief Returns a constant iterator past the last temporal slice + */ const_iterator end() const { return { *this, _tdomain->cend() }; } + /** + * \brief Constant reverse iterator over the slices of this tube + */ struct const_reverse_iterator : public base_container::const_reverse_iterator { public: @@ -706,7 +1219,14 @@ namespace codac2 const SlicedTube& _x; }; + /** + * \brief Returns a constant reverse iterator to the last temporal slice + */ const_reverse_iterator rbegin() const { return { *this, _tdomain->crbegin() }; } + + /** + * \brief Returns a constant reverse iterator past the first temporal slice + */ const_reverse_iterator rend() const { return { *this, _tdomain->crend() }; } }; @@ -720,6 +1240,10 @@ namespace codac2 template SlicedTube(const std::shared_ptr& tdomain, const SampledTraj& f) -> SlicedTube::Domain>; + + template + SlicedTube(const std::shared_ptr& tdomain, const AnalyticTraj& f) -> + SlicedTube::Domain>; // Ctc @@ -727,8 +1251,8 @@ namespace codac2 template inline void CtcBase::contract(SlicedTube&... x) const { - auto tdomain = std::get<0>(std::make_tuple(x...)); - for(auto it = tdomain.begin() ; it != tdomain.end() ; it++) + auto&& x0 = std::get<0>(std::forward_as_tuple(x...)); + for(auto it = x0.begin(); it != x0.end(); ++it) contract((x.slice(it)->codomain())...); } diff --git a/src/core/domains/tube/codac2_SlicedTubeBase.h b/src/core/domains/tube/codac2_SlicedTubeBase.h index 8616ef77f..072b653de 100644 --- a/src/core/domains/tube/codac2_SlicedTubeBase.h +++ b/src/core/domains/tube/codac2_SlicedTubeBase.h @@ -14,25 +14,70 @@ namespace codac2 { + /** + * \class SlicedTubeBase + * \brief Base class for tubes defined over a sliced temporal domain + * + * A ``SlicedTubeBase`` is a tube whose temporal domain is represented by a + * ``TDomain`` made of ``TSlice`` objects. + * + * This class provides common services for all sliced tubes: + * - access to the number of temporal elements, + * - access to the first and last codomain slices, + * - cleanup of the links stored in the temporal partition when the tube is + * destroyed. + * + * Each ``TSlice`` of the associated ``TDomain`` stores a map from + * ``SlicedTubeBase*`` to ``SliceBase``. The destructor of this class removes + * the entries associated with the current tube. + */ class SlicedTubeBase : public TubeBase { public: + /** + * \brief Creates a sliced tube over a given temporal domain + * + * \param tdomain shared temporal domain of this tube + */ SlicedTubeBase(const std::shared_ptr& tdomain) : TubeBase(tdomain) { } + /** + * \brief Destroys this sliced tube + * + * All references to this tube stored in the ``TSlice`` objects of the + * associated ``TDomain`` are removed. + */ inline ~SlicedTubeBase() { for(auto& s : *_tdomain) s._slices.erase(this); } + /** + * \brief Returns the number of temporal elements of this tube + * + * The count includes all elements stored in the temporal partition, + * including explicit gates if they exist. + * + * \return number of temporal slices of the associated ``TDomain`` + */ inline size_t nb_slices() const { return _tdomain->nb_tslices(); } + /** + * \brief Returns the first slice of this tube + * + * \note This method assumes that the associated temporal domain is not + * empty and that its first ``TSlice`` contains a slice attached to + * this tube. + * + * \return shared pointer to the first slice + */ inline std::shared_ptr first_slice() const { const auto& slices = _tdomain->front().slices(); @@ -40,6 +85,15 @@ namespace codac2 return slices.at(this); } + /** + * \brief Returns the last slice of this tube + * + * \note This method assumes that the associated temporal domain is not + * empty and that its last ``TSlice`` contains a slice attached to + * this tube. + * + * \return shared pointer to the last slice + */ inline std::shared_ptr last_slice() const { const auto& slices = _tdomain->back().slices(); diff --git a/src/core/domains/tube/codac2_SlicedTube_operations.cpp b/src/core/domains/tube/codac2_SlicedTube_operations.cpp index 309e86762..d9b8fcfe2 100644 --- a/src/core/domains/tube/codac2_SlicedTube_operations.cpp +++ b/src/core/domains/tube/codac2_SlicedTube_operations.cpp @@ -53,7 +53,7 @@ namespace codac2 macro_unary_tube(atan) SlicedTube atan2(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(atan2) + macro_binary_tube_tube(atan2,x1) SlicedTube atan2(const SlicedTube& x1, const Interval& x2) macro_binary_tube_real(atan2) @@ -83,7 +83,7 @@ namespace codac2 macro_unary_tube(abs) SlicedTube min(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(min) + macro_binary_tube_tube(min,x1) SlicedTube min(const SlicedTube& x1, const Interval& x2) macro_binary_tube_real(min) @@ -92,7 +92,7 @@ namespace codac2 macro_binary_real_tube(min) SlicedTube max(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(max) + macro_binary_tube_tube(max,x1) SlicedTube max(const SlicedTube& x1, const Interval& x2) macro_binary_tube_real(max) diff --git a/src/core/domains/tube/codac2_SlicedTube_operations.h b/src/core/domains/tube/codac2_SlicedTube_operations.h index a5c201cf5..f9e46d761 100644 --- a/src/core/domains/tube/codac2_SlicedTube_operations.h +++ b/src/core/domains/tube/codac2_SlicedTube_operations.h @@ -9,6 +9,8 @@ #pragma once +#include + #include "codac2_SlicedTube.h" #include "codac2_math.h" @@ -23,16 +25,16 @@ namespace codac2 sy->codomain() = f(sy->codomain()); \ } \ return y; \ - }; \ + } \ - #define macro_binary_tube_tube(f) \ + #define macro_binary_tube_tube(f,output_type) \ { \ assert_release(x1.tdomain() == x2.tdomain()); \ - auto y = x2; \ + auto y = output_type; \ for(auto it = y.tdomain()->begin() ; it != y.tdomain()->end() ; it++) \ { \ auto sy = y.slice(it); \ - sy->codomain() = f(x1.slice(it)->codomain(),sy->codomain()); \ + sy->codomain() = f(x1.slice(it)->codomain(), x2.slice(it)->codomain()); \ } \ return y; \ } \ @@ -43,7 +45,7 @@ namespace codac2 for(auto it = y.tdomain()->begin() ; it != y.tdomain()->end() ; it++) \ { \ auto sy = y.slice(it); \ - sy->codomain() = f(x1,sy->codomain()); \ + sy->codomain() = f(x1, sy->codomain()); \ } \ return y; \ } \ @@ -54,7 +56,7 @@ namespace codac2 for(auto it = y.tdomain()->begin() ; it != y.tdomain()->end() ; it++) \ { \ auto sy = y.slice(it); \ - sy->codomain() = f(sy->codomain(),x2); \ + sy->codomain() = f(sy->codomain(), x2); \ } \ return y; \ } \ @@ -65,7 +67,7 @@ namespace codac2 for(auto it = x1.tdomain()->begin() ; it != x1.tdomain()->end() ; it++) \ { \ auto sx1 = x1.slice(it); \ - sx1->codomain() = f(sx1->codomain(),x2.slice(it)->codomain()); \ + sx1->codomain() = f(sx1->codomain(), x2.slice(it)->codomain()); \ } \ return x1; \ } \ @@ -75,456 +77,930 @@ namespace codac2 for(auto it = x1.tdomain()->begin() ; it != x1.tdomain()->end() ; it++) \ { \ auto sx1 = x1.slice(it); \ - sx1->codomain() = f(sx1->codomain(),x2); \ + sx1->codomain() = f(sx1->codomain(), x2); \ } \ return x1; \ } \ - template + template + struct is_slicedtube : std::false_type {}; + + template + struct is_slicedtube> : std::true_type {}; + + template + inline constexpr bool is_slicedtube_v = is_slicedtube>::value; + + template + concept NonSlicedTube = !is_slicedtube_v; + + template + concept NonScalarTubeCodomain = + !std::is_same_v, double> && + !std::is_same_v, Interval>; + + template + inline T operator_tube_union(const T& x1, const T& x2) { return x1 | x2; } + + template + inline T operator_tube_intersection(const T& x1, const T& x2) { return x1 & x2; } + + template inline T operator_tube_add(const X1& x1, const X2& x2) { return x1 + x2; } - template + template inline T operator_tube_sub(const X1& x1, const X2& x2) { return x1 - x2; } - template + template inline T operator_tube_mul(const X1& x1, const X2& x2) { return x1 * x2; } - template - requires (!std::is_same_v) - inline T operator_tube_mul_scal(const Interval& x1, const X2& x2) { return x1 * x2; } + template + inline T operator_tube_scal_mul(const Interval& x1, const T& x2) { return x1 * x2; } - template - inline T operator_tube_mul_scal(const X1& x1, const Interval& x2) { return x1 * x2; } + template + inline T operator_tube_mul_scal(const T& x1, const Interval& x2) { return x1 * x2; } - inline IntervalMatrix operator_tube_mul_vec(const IntervalMatrix& x1, const IntervalVector& x2) { return x1 * x2; } + inline IntervalVector operator_tube_mul_vec(const IntervalMatrix& x1, const IntervalVector& x2) { return x1 * x2; } - template + template inline T operator_tube_div(const X1& x1, const X2& x2) { return x1 / x2; } - template + template inline T operator_tube_div_scal(const X1& x1, const Interval& x2) { return x1 / x2; } - /** \brief \f$x1(\cdot)\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise hull-union assignment of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\sqcup[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)\sqcup[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\sqcup[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand, updated in place. + * \param x2 right operand. + * \return \p x1 after the pointwise hull-union. + */ + template + inline SlicedTube& operator|=(SlicedTube& x1, const SlicedTube& x2) + macro_member_binary_tube_tube(operator_tube_union); + + /** + * \brief Pointwise hull-union of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\sqcup[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)\sqcup[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\sqcup[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand. + * \param x2 right operand. + * \return The pointwise hull-union tube. + */ template - inline const SlicedTube& operator+(const SlicedTube& x1) { + inline SlicedTube operator|(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_union,x1); + + /** + * \brief Pointwise intersection assignment of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\cap[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)\cap[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\cap[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand, updated in place. + * \param x2 right operand. + * \return \p x1 after the pointwise intersection. + */ + template + inline SlicedTube& operator&=(SlicedTube& x1, const SlicedTube& x2) + macro_member_binary_tube_tube(operator_tube_intersection); + + /** + * \brief Pointwise intersection of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\cap[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)\cap[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\cap[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand. + * \param x2 right operand. + * \return The pointwise intersection tube. + */ + template + inline SlicedTube operator&(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_intersection,x1); + + /** + * \brief Unary plus. + * + * According to \p T, the operand is respectively denoted + * \f$[x_1](\cdot)\f$, \f$[\mathbf{x}_1](\cdot)\f$ or \f$[\mathbf{A}_1](\cdot)\f$. + * + * \param x1 operand. + * \return \p x1 unchanged. + */ + template + inline const SlicedTube& operator+(const SlicedTube& x1) + { return x1; } - /** \brief \f$x_1(\cdot)+x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise sum of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)+[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)+[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)+[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand. + * \param x2 right operand. + * \return The pointwise sum tube. + */ template inline SlicedTube operator+(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(operator_tube_add); - - /** \brief \f$x_1(\cdot)+x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + macro_binary_tube_tube(operator_tube_add,x1); + + /** + * \brief Pointwise sum of a tube and a constant object with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)+x_2\f$, + * \f$[\mathbf{x}_1](\cdot)+\mathbf{x}_2\f$ or + * \f$[\mathbf{A}_1](\cdot)+\mathbf{A}_2\f$. + * + * \param x1 left tube operand. + * \param x2 right constant operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator+(const SlicedTube& x1, const Q& x2) macro_binary_tube_real(operator_tube_add); - /** \brief \f$x+x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + /** + * \brief Pointwise sum of a constant object and a tube with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$x_1+[x_2](\cdot)\f$, + * \f$\mathbf{x}_1+[\mathbf{x}_2](\cdot)\f$ or + * \f$\mathbf{A}_1+[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left constant operand. + * \param x2 right tube operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator+(const Q& x1, const SlicedTube& x2) macro_binary_real_tube(operator_tube_add); /** - * \brief Operates += - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise addition assignment with a constant object of matching codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right constant operand. + * \return \p x1 after the pointwise addition. */ - template + template + requires NonSlicedTube inline SlicedTube& operator+=(SlicedTube& x1, const Q& x2) macro_member_binary_tube_real(operator_tube_add); /** - * \brief Operates += - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise addition assignment with a tube of the same codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right tube operand. + * \return \p x1 after the pointwise addition. */ template inline SlicedTube& operator+=(SlicedTube& x1, const SlicedTube& x2) macro_member_binary_tube_tube(operator_tube_add); - /** \brief \f$-x_1(\cdot)\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Unary minus. + * + * According to \p T, the operand is respectively denoted + * \f$[x_1](\cdot)\f$, \f$[\mathbf{x}_1](\cdot)\f$ or \f$[\mathbf{A}_1](\cdot)\f$. + * + * \param x1 operand. + * \return The pointwise opposite tube. + */ template - inline SlicedTube operator-(const SlicedTube& x1) { - return -1.*x1; + inline SlicedTube operator-(const SlicedTube& x1) + { + return -1. * x1; } - /** \brief \f$x_1(\cdot)-x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise difference of two tubes with the same codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)-[x_2](\cdot)\f$, + * \f$[\mathbf{x}_1](\cdot)-[\mathbf{x}_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)-[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left operand. + * \param x2 right operand. + * \return The pointwise difference tube. + */ template inline SlicedTube operator-(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(operator_tube_sub); - - /** \brief \f$x_1(\cdot)-x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + macro_binary_tube_tube(operator_tube_sub,x1); + + /** + * \brief Pointwise difference between a tube and a constant object with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)-x_2\f$, + * \f$[\mathbf{x}_1](\cdot)-\mathbf{x}_2\f$ or + * \f$[\mathbf{A}_1](\cdot)-\mathbf{A}_2\f$. + * + * \param x1 left tube operand. + * \param x2 right constant operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator-(const SlicedTube& x1, const Q& x2) macro_binary_tube_real(operator_tube_sub); - /** \brief \f$x-x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + /** + * \brief Pointwise difference between a constant object and a tube with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$x_1-[x_2](\cdot)\f$, + * \f$\mathbf{x}_1-[\mathbf{x}_2](\cdot)\f$ or + * \f$\mathbf{A}_1-[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left constant operand. + * \param x2 right tube operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator-(const Q& x1, const SlicedTube& x2) macro_binary_real_tube(operator_tube_sub); /** - * \brief Operates -= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise subtraction assignment with a constant object of matching codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right constant operand. + * \return \p x1 after the pointwise subtraction. */ - template + template + requires NonSlicedTube inline SlicedTube& operator-=(SlicedTube& x1, const Q& x2) macro_member_binary_tube_real(operator_tube_sub); /** - * \brief Operates -= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise subtraction assignment with a tube of the same codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right tube operand. + * \return \p x1 after the pointwise subtraction. */ template inline SlicedTube& operator-=(SlicedTube& x1, const SlicedTube& x2) macro_member_binary_tube_tube(operator_tube_sub); - /** \brief \f$x_1\cdot x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise multiplication of a scalar interval and a tube. + * + * According to \p T, the operation is respectively + * \f$x_1\,[x_2](\cdot)\f$, + * \f$x_1\,[\mathbf{x}_2](\cdot)\f$ or + * \f$x_1\,[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 scalar interval. + * \param x2 right tube operand. + * \return The resulting tube. + */ template - requires (!std::is_same_v) + requires (!std::is_same_v) inline SlicedTube operator*(const Interval& x1, const SlicedTube& x2) - macro_binary_real_tube(operator_tube_mul_scal); + macro_binary_real_tube(operator_tube_scal_mul); - /** \brief \f$x_1(\cdot)\cdot x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise multiplication of a tube by a scalar interval. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\,x_2\f$, + * \f$[\mathbf{x}_1](\cdot)\,x_2\f$ or + * \f$[\mathbf{A}_1](\cdot)\,x_2\f$. + * + * \param x1 left tube operand. + * \param x2 scalar interval. + * \return The resulting tube. + */ template - requires (!std::is_same_v) + requires (!std::is_same_v) inline SlicedTube operator*(const SlicedTube& x1, const Interval& x2) macro_binary_tube_real(operator_tube_mul_scal); - /** \brief \f$x_1(\cdot)\cdot x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise multiplication of two scalar interval tubes. + * + * \f$[x_1](\cdot)\,[x_2](\cdot)\f$ + * + * \param x1 left operand. + * \param x2 right operand. + * \return The resulting scalar interval tube. + */ + inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_mul,x1); + + /** + * \brief Pointwise multiplication of two interval-vector tubes. + * + * \f$[\mathbf{x}_1](\cdot)\,[\mathbf{x}_2](\cdot)\f$ + * + * \param x1 left operand. + * \param x2 right operand. + * \return The resulting interval-vector tube. + */ + inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_mul,x1); + + /** + * \brief Pointwise multiplication of two interval-matrix tubes. + * + * \f$[\mathbf{A}_1](\cdot)\,[\mathbf{A}_2](\cdot)\f$ + * + * \param x1 left operand. + * \param x2 right operand. + * \return The resulting interval-matrix tube. + */ + inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_mul,x1); + + /** + * \brief Pointwise multiplication of a scalar interval tube and a non-scalar tube. + * + * Depending on \p T, the operation is + * \f$[x_1](\cdot)\,[\mathbf{x}_2](\cdot)\f$ or + * \f$[x_1](\cdot)\,[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 scalar interval tube. + * \param x2 non-scalar tube operand. + * \return The resulting tube. + */ + template + requires NonScalarTubeCodomain + inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_mul,x2); + + /** + * \brief Pointwise multiplication of a non-scalar tube and a scalar interval tube. + * + * Depending on \p T, the operation is + * \f$[\mathbf{x}_1](\cdot)\,[x_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\,[x_2](\cdot)\f$. + * + * \param x1 non-scalar tube operand. + * \param x2 scalar interval tube. + * \return The resulting tube. + */ template - inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(operator_tube_mul); - - /** \brief \f$x_1(\cdot)\cdot x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + requires NonScalarTubeCodomain + inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_mul,x1); + + /** + * \brief Pointwise multiplication of a tube and a constant object with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)\,x_2\f$, + * \f$[\mathbf{x}_1](\cdot)\,\mathbf{x}_2\f$ or + * \f$[\mathbf{A}_1](\cdot)\,\mathbf{A}_2\f$. + * + * \param x1 left tube operand. + * \param x2 right constant operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator*(const SlicedTube& x1, const Q& x2) macro_binary_tube_real(operator_tube_mul); - /** \brief \f$x\cdot x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + /** + * \brief Pointwise multiplication of a constant object and a tube with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$x_1\,[x_2](\cdot)\f$, + * \f$\mathbf{x}_1\,[\mathbf{x}_2](\cdot)\f$ or + * \f$\mathbf{A}_1\,[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 left constant operand. + * \param x2 right tube operand. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator*(const Q& x1, const SlicedTube& x2) macro_binary_real_tube(operator_tube_mul); - /** \brief \f$x_1(\cdot)\cdot x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise multiplication of an interval-matrix tube by an interval-vector tube. + * + * \f$[\mathbf{A}_1](\cdot)\,[\mathbf{x}_2](\cdot)\f$ + * + * \param x1 interval-matrix tube. + * \param x2 interval-vector tube. + * \return The resulting interval-vector tube. + */ inline SlicedTube operator*(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(operator_tube_mul_vec); + macro_binary_tube_tube(operator_tube_mul_vec,x2); /** - * \brief Operates *= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise multiplication assignment with a constant object of matching codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right constant operand. + * \return \p x1 after the pointwise multiplication. */ - template + template + requires NonSlicedTube inline SlicedTube& operator*=(SlicedTube& x1, const Q& x2) macro_member_binary_tube_real(operator_tube_mul); /** - * \brief Operates *= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise multiplication assignment with a tube of the same codomain type. + * + * \param x1 left tube operand, updated in place. + * \param x2 right tube operand. + * \return \p x1 after the pointwise multiplication. */ template inline SlicedTube& operator*=(SlicedTube& x1, const SlicedTube& x2) macro_member_binary_tube_tube(operator_tube_mul); - /** \brief \f$x_2(\cdot)/x_1\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise multiplication assignment by a scalar interval tube. + * + * Depending on \p T, the operation is + * \f$[\mathbf{x}_1](\cdot)\,[x_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)\,[x_2](\cdot)\f$. + * + * \param x1 non-scalar tube operand, updated in place. + * \param x2 scalar interval tube. + * \return \p x1 after the pointwise multiplication. + */ + template + requires NonScalarTubeCodomain + inline SlicedTube& operator*=(SlicedTube& x1, const SlicedTube& x2) + macro_member_binary_tube_tube(operator_tube_mul); + + /** + * \brief Pointwise division of a tube by a scalar interval. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)/x_2\f$, + * \f$[\mathbf{x}_1](\cdot)/x_2\f$ or + * \f$[\mathbf{A}_1](\cdot)/x_2\f$. + * + * \param x1 numerator tube. + * \param x2 scalar interval denominator. + * \return The resulting tube. + */ template - requires (!std::is_same_v) + requires (!std::is_same_v) inline SlicedTube operator/(const SlicedTube& x1, const Interval& x2) macro_binary_tube_real(operator_tube_div_scal); - /** \brief \f$x_1(\cdot)/x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise division of two scalar interval tubes. + * + * \f$[x_1](\cdot)/[x_2](\cdot)\f$ + * + * \param x1 numerator tube. + * \param x2 denominator tube. + * \return The resulting scalar interval tube. + */ + inline SlicedTube operator/(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_div,x1); + + /** + * \brief Pointwise division of a non-scalar tube by a scalar interval tube. + * + * Depending on \p T, the operation is + * \f$[\mathbf{x}_1](\cdot)/[x_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)/[x_2](\cdot)\f$. + * + * \param x1 non-scalar numerator tube. + * \param x2 scalar interval denominator tube. + * \return The resulting tube. + */ template - inline SlicedTube operator/(const SlicedTube& x1, const SlicedTube& x2) - macro_binary_tube_tube(operator_tube_div); - - /** \brief \f$x_1(\cdot)/x_2\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + requires NonScalarTubeCodomain + inline SlicedTube operator/(const SlicedTube& x1, const SlicedTube& x2) + macro_binary_tube_tube(operator_tube_div,x1); + + /** + * \brief Pointwise division of a tube by a constant object with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$[x_1](\cdot)/x_2\f$, + * \f$[\mathbf{x}_1](\cdot)/\mathbf{x}_2\f$ or + * \f$[\mathbf{A}_1](\cdot)/\mathbf{A}_2\f$. + * + * \param x1 numerator tube. + * \param x2 denominator constant object. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator/(const SlicedTube& x1, const Q& x2) macro_binary_tube_real(operator_tube_div); - /** \brief \f$x/x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ - template + /** + * \brief Pointwise division of a constant object by a tube with matching codomain type. + * + * According to \p T, the operation is respectively + * \f$x_1/[x_2](\cdot)\f$, + * \f$\mathbf{x}_1/[\mathbf{x}_2](\cdot)\f$ or + * \f$\mathbf{A}_1/[\mathbf{A}_2](\cdot)\f$. + * + * \param x1 numerator constant object. + * \param x2 denominator tube. + * \return The resulting tube. + */ + template + requires NonSlicedTube inline SlicedTube operator/(const Q& x1, const SlicedTube& x2) macro_binary_real_tube(operator_tube_div); /** - * \brief Operates /= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise division assignment with a constant object of matching codomain type. + * + * \param x1 numerator tube, updated in place. + * \param x2 denominator constant object. + * \return \p x1 after the pointwise division. */ - template + template + requires NonSlicedTube inline SlicedTube& operator/=(SlicedTube& x1, const Q& x2) macro_member_binary_tube_real(operator_tube_div); /** - * \brief Operates /= - * \param x1 - * \param x2 - * \return updated output + * \brief Pointwise division assignment with a tube of the same codomain type. + * + * \param x1 numerator tube, updated in place. + * \param x2 denominator tube. + * \return \p x1 after the pointwise division. */ template inline SlicedTube& operator/=(SlicedTube& x1, const SlicedTube& x2) macro_member_binary_tube_tube(operator_tube_div); - /** \brief \f$x^2(\cdot)\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise division assignment by a scalar interval tube. + * + * Depending on \p T, the operation is + * \f$[\mathbf{x}_1](\cdot)/[x_2](\cdot)\f$ or + * \f$[\mathbf{A}_1](\cdot)/[x_2](\cdot)\f$. + * + * \param x1 non-scalar numerator tube, updated in place. + * \param x2 scalar interval denominator tube. + * \return \p x1 after the pointwise division. + */ + template + requires NonScalarTubeCodomain + inline SlicedTube& operator/=(SlicedTube& x1, const SlicedTube& x2) + macro_member_binary_tube_tube(operator_tube_div); + + /** + * \brief Pointwise square of a scalar interval tube. + * + * \f$\big([x_1](\cdot)\big)^2\f$ + * + * \param x1 scalar interval tube. + * \return The squared tube. + */ SlicedTube sqr(const SlicedTube& x1); - /** \brief \f$\sqrt{x_1(\cdot)}\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise square root of a scalar interval tube. + * + * \f$\sqrt{[x_1](\cdot)}\f$ + * + * \param x1 scalar interval tube. + * \return The square-root tube. + */ SlicedTube sqrt(const SlicedTube& x1); - /** \brief \f$x^x_2(\cdot)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise power of a scalar interval tube with an integer exponent. + * + * \f$\big([x_1](\cdot)\big)^{x_2}\f$ + * + * \param x1 scalar interval tube. + * \param x2 integer exponent. + * \return The resulting tube. + */ + SlicedTube pow(const SlicedTube& x1, int x2); + + /** + * \brief Pointwise power of a scalar interval tube with an interval exponent. + * + * \f$\big([x_1](\cdot)\big)^{x_2}\f$ + * + * \param x1 scalar interval tube. + * \param x2 interval exponent. + * \return The resulting tube. + */ SlicedTube pow(const SlicedTube& x1, const Interval& x2); - /** \brief \f$\exp(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise exponential of a scalar interval tube. + * + * \f$\exp\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube exp(const SlicedTube& x1); - /** \brief \f$\log(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise natural logarithm of a scalar interval tube. + * + * \f$\log\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube log(const SlicedTube& x1); - /** \brief \f$\cos(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise cosine of a scalar interval tube. + * + * \f$\cos\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube cos(const SlicedTube& x1); - /** \brief \f$\sin(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise sine of a scalar interval tube. + * + * \f$\sin\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube sin(const SlicedTube& x1); - /** \brief \f$\tan(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise tangent of a scalar interval tube. + * + * \f$\tan\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube tan(const SlicedTube& x1); - /** \brief \f$\arccos(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise arc-cosine of a scalar interval tube. + * + * \f$\arccos\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube acos(const SlicedTube& x1); - /** \brief \f$\arcsin(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise arc-sine of a scalar interval tube. + * + * \f$\arcsin\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube asin(const SlicedTube& x1); - /** \brief \f$\arctan(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise arc-tangent of a scalar interval tube. + * + * \f$\arctan\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube atan(const SlicedTube& x1); - /** \brief \f$\mathrm{arctan2}(x_1(\cdot),x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise two-argument arc-tangent of two scalar interval tubes. + * + * \f$\operatorname{atan2}\!\big([x_1](\cdot),[x_2](\cdot)\big)\f$ + * + * \param x1 first scalar interval tube. + * \param x2 second scalar interval tube. + * \return The resulting tube. + */ SlicedTube atan2(const SlicedTube& x1, const SlicedTube& x2); - /** \brief \f$\mathrm{arctan2}(x_1(\cdot),x_2)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise two-argument arc-tangent of a scalar interval tube and an interval. + * + * \f$\operatorname{atan2}\!\big([x_1](\cdot),x_2\big)\f$ + * + * \param x1 scalar interval tube. + * \param x2 scalar interval. + * \return The resulting tube. + */ SlicedTube atan2(const SlicedTube& x1, const Interval& x2); - /** \brief \f$\mathrm{arctan2}(x_1, x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise two-argument arc-tangent of an interval and a scalar interval tube. + * + * \f$\operatorname{atan2}\!\big(x_1,[x_2](\cdot)\big)\f$ + * + * \param x1 scalar interval. + * \param x2 scalar interval tube. + * \return The resulting tube. + */ SlicedTube atan2(const Interval& x1, const SlicedTube& x2); - /** \brief \f$\cosh(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise hyperbolic cosine of a scalar interval tube. + * + * \f$\cosh\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube cosh(const SlicedTube& x1); - /** \brief \f$\sinh(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise hyperbolic sine of a scalar interval tube. + * + * \f$\sinh\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube sinh(const SlicedTube& x1); - /** \brief \f$\tanh(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise hyperbolic tangent of a scalar interval tube. + * + * \f$\tanh\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube tanh(const SlicedTube& x1); - /** \brief \f$\mathrm{arccosh}(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise inverse hyperbolic cosine of a scalar interval tube. + * + * \f$\operatorname{arccosh}\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube acosh(const SlicedTube& x1); - /** \brief \f$\mathrm{arcsinh}(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise inverse hyperbolic sine of a scalar interval tube. + * + * \f$\operatorname{arcsinh}\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube asinh(const SlicedTube& x1); - /** \brief \f$\mathrm{arctanh}(x_1(\cdot))\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise inverse hyperbolic tangent of a scalar interval tube. + * + * \f$\operatorname{arctanh}\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube atanh(const SlicedTube& x1); - /** \brief \f$\mid x_1(\cdot)\mid\f$ - * \param x1 - * \return tube output - */ + /** + * \brief Pointwise absolute value of a scalar interval tube. + * + * \f$\left|[x_1](\cdot)\right|\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube abs(const SlicedTube& x1); - /** \brief \f$\min(x_1(\cdot),x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise minimum of two scalar interval tubes. + * + * \f$\min\!\big([x_1](\cdot),[x_2](\cdot)\big)\f$ + * + * \param x1 first scalar interval tube. + * \param x2 second scalar interval tube. + * \return The resulting tube. + */ SlicedTube min(const SlicedTube& x1, const SlicedTube& x2); - /** \brief \f$\min(x_1(\cdot),x_2)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise minimum of a scalar interval tube and an interval. + * + * \f$\min\!\big([x_1](\cdot),x_2\big)\f$ + * + * \param x1 scalar interval tube. + * \param x2 scalar interval. + * \return The resulting tube. + */ SlicedTube min(const SlicedTube& x1, const Interval& x2); - /** \brief \f$\min(x_1, x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise minimum of an interval and a scalar interval tube. + * + * \f$\min\!\big(x_1,[x_2](\cdot)\big)\f$ + * + * \param x1 scalar interval. + * \param x2 scalar interval tube. + * \return The resulting tube. + */ SlicedTube min(const Interval& x1, const SlicedTube& x2); - /** \brief \f$\max(x_1(\cdot),x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise maximum of two scalar interval tubes. + * + * \f$\max\!\big([x_1](\cdot),[x_2](\cdot)\big)\f$ + * + * \param x1 first scalar interval tube. + * \param x2 second scalar interval tube. + * \return The resulting tube. + */ SlicedTube max(const SlicedTube& x1, const SlicedTube& x2); - /** \brief \f$\max(x_1(\cdot),x_2)\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise maximum of a scalar interval tube and an interval. + * + * \f$\max\!\big([x_1](\cdot),x_2\big)\f$ + * + * \param x1 scalar interval tube. + * \param x2 scalar interval. + * \return The resulting tube. + */ SlicedTube max(const SlicedTube& x1, const Interval& x2); - /** \brief \f$\max(x_1, x_2(\cdot))\f$ - * \param x1 - * \param x2 - * \return tube output - */ + /** + * \brief Pointwise maximum of an interval and a scalar interval tube. + * + * \f$\max\!\big(x_1,[x_2](\cdot)\big)\f$ + * + * \param x1 scalar interval. + * \param x2 scalar interval tube. + * \return The resulting tube. + */ SlicedTube max(const Interval& x1, const SlicedTube& x2); + /** + * \brief Pointwise sign of a scalar interval tube. + * + * \f$\operatorname{sign}\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube sign(const SlicedTube& x1); + /** + * \brief Pointwise integer-part operator applied to a scalar interval tube. + * + * \f$\operatorname{integer}\!\big([x_1](\cdot)\big)\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube integer(const SlicedTube& x1); + /** + * \brief Pointwise floor of a scalar interval tube. + * + * \f$\lfloor [x_1](\cdot) \rfloor\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube floor(const SlicedTube& x1); + /** + * \brief Pointwise ceiling of a scalar interval tube. + * + * \f$\lceil [x_1](\cdot) \rceil\f$ + * + * \param x1 scalar interval tube. + * \return The resulting tube. + */ SlicedTube ceil(const SlicedTube& x1); } \ No newline at end of file diff --git a/src/core/domains/tube/codac2_TDomain.cpp b/src/core/domains/tube/codac2_TDomain.cpp index 8b2a742a8..3d6260949 100644 --- a/src/core/domains/tube/codac2_TDomain.cpp +++ b/src/core/domains/tube/codac2_TDomain.cpp @@ -58,32 +58,22 @@ namespace codac2 bool TDomain::all_gates_defined() const { - if(t0_tf().is_degenerated()) - return true; - - else if(nb_tslices() == 1) + if(this->empty()) return false; - - else + + bool expect_gate = true; + + for(const auto& s : *this) { - list::const_iterator it = std::next(this->begin()); - while(it != this->end()) - { - if(it->is_gate()) - return false; - - it++; - - if(it != this->end()) - { - if(!it->is_gate()) - return false; - it++; - } - } - - return true; + if(s.is_gate() != expect_gate) + return false; + + expect_gate = !expect_gate; } + + // We must end on a gate: + // valid patterns are [gate] or [gate, slice, ..., gate] + return !expect_gate; } std::vector TDomain::tslices_vector() const @@ -212,6 +202,7 @@ namespace codac2 void TDomain::truncate(const Interval& new_tdomain) { + assert_release(new_tdomain.is_subset(t0_tf())); sample(new_tdomain.lb()); sample(new_tdomain.ub()); this->remove_if( diff --git a/src/core/domains/tube/codac2_TSlice.cpp b/src/core/domains/tube/codac2_TSlice.cpp index 778e241e6..0377e24ad 100644 --- a/src/core/domains/tube/codac2_TSlice.cpp +++ b/src/core/domains/tube/codac2_TSlice.cpp @@ -35,14 +35,4 @@ namespace codac2 { return _slices; } - - bool TSlice::operator==(const TSlice& x) const - { - return Interval::operator==(x); - } - - bool TSlice::operator==(const Interval& x) const - { - return Interval::operator==(x); - } } \ No newline at end of file diff --git a/src/core/domains/tube/codac2_TSlice.h b/src/core/domains/tube/codac2_TSlice.h index 11d95c8de..81edf9c59 100644 --- a/src/core/domains/tube/codac2_TSlice.h +++ b/src/core/domains/tube/codac2_TSlice.h @@ -18,19 +18,68 @@ namespace codac2 class SliceBase; class SlicedTubeBase; + /** + * \class TSlice + * \brief Temporal slice shared by sliced tubes. + * + * A ``TSlice`` represents a temporal interval of a ``TDomain``. + * It inherits from ``Interval`` and may correspond either to: + * - a non-degenerate time slice \f$[t_i,t_{i+1}]\f$, or + * - a degenerate interval \f$[t_i,t_i]\f$, referred to as a gate. + * + * For each sliced tube defined on the same temporal partition, this object + * stores the corresponding ``SliceBase`` instance. + */ class TSlice : public Interval { public: + /** + * \brief Creates a temporal slice over the given time interval + * + * \param tdomain temporal interval associated with this slice + */ explicit TSlice(const Interval& tdomain); + + /** + * \brief Creates a temporal slice from another one over a new time interval + * + * This constructor duplicates the slice container associated with each + * tube by performing a deep copy of the underlying ``SliceBase`` objects. + * + * \param tslice source temporal slice + * \param tdomain temporal interval associated with the copied slice + */ TSlice(const TSlice& tslice, const Interval& tdomain); // performs a deep copy on slices + + /** + * \brief Tests whether this temporal slice is a gate + * + * A temporal slice is considered as a gate when its temporal interval is + * degenerate, i.e., when \f$t^-=t^+\f$. + * + * \return ``true`` if this slice is a gate, ``false`` otherwise + */ bool is_gate() const; + + /** + * \brief Returns the slices attached to this temporal slice + * + * The returned map associates each ``SlicedTubeBase`` defined on the same + * temporal partition with its corresponding ``SliceBase`` over this + * temporal interval. + * + * \return constant reference to the map of attached slices + */ const std::map>& slices() const; - bool operator==(const TSlice& x) const; - bool operator==(const Interval& x) const; + + using Interval::operator==; protected: + /** + * \brief Slices attached to each sliced tube over this temporal interval + */ std::map> _slices; friend class TDomain; diff --git a/src/core/domains/tube/codac2_TubeBase.h b/src/core/domains/tube/codac2_TubeBase.h index fde7ac85e..b3bec8b28 100644 --- a/src/core/domains/tube/codac2_TubeBase.h +++ b/src/core/domains/tube/codac2_TubeBase.h @@ -14,19 +14,50 @@ namespace codac2 { + /** + * \class TubeBase + * \brief Base class for tubes defined over a temporal domain + * + * A ``TubeBase`` stores the temporal domain shared by a tube. + * This temporal domain is represented by a ``TDomain`` object and defines the + * global time interval \f$[t_0,t_f]\f$ on which the tube is defined. + * + * This class provides common services for all tube types: + * - access to the underlying temporal domain, + * - access to its global temporal interval. + */ class TubeBase : public Domain { public: - TubeBase(const std::shared_ptr& tdomain) + /** + * \brief Creates a tube over a given temporal domain + * + * \param tdomain shared temporal domain of this tube + */ + explicit TubeBase(const std::shared_ptr& tdomain) : _tdomain(tdomain) - { } + { + assert_release(tdomain != nullptr); + } + /** + * \brief Returns the temporal domain of this tube + * + * \return shared pointer to the associated ``TDomain`` + */ inline const std::shared_ptr& tdomain() const { return _tdomain; } + /** + * \brief Returns the global temporal interval of this tube + * + * This method is a shortcut to ``tdomain()->t0_tf()``. + * + * \return temporal interval \f$[t_0,t_f]\f$ of this tube + */ inline Interval t0_tf() const { return _tdomain->t0_tf(); @@ -34,6 +65,9 @@ namespace codac2 protected: + /** + * \brief Shared temporal domain of this tube + */ const std::shared_ptr _tdomain; }; } \ No newline at end of file diff --git a/src/core/domains/tube/codac2_tube_cart_prod.h b/src/core/domains/tube/codac2_tube_cart_prod.h index 0ed3a947e..ac4083647 100644 --- a/src/core/domains/tube/codac2_tube_cart_prod.h +++ b/src/core/domains/tube/codac2_tube_cart_prod.h @@ -9,18 +9,39 @@ #pragma once +#include #include "codac2_cart_prod.h" #include "codac2_SlicedTube.h" namespace codac2 { + /** + * \brief Computes the Cartesian product of several sliced tubes + * + * This function builds a vector-valued sliced tube whose temporal domain is + * shared with the input tubes. For each temporal slice, the codomain of the + * resulting tube is the Cartesian product of the codomains of the input tubes + * over the same temporal slice. + * + * The dimension of the resulting codomain is equal to the sum of the + * dimensions of all input tubes. + * + * \note All input tubes must share the same temporal partition. + * + * \tparam X types of the input sliced tubes + * \param x input sliced tubes + * \return a sliced tube enclosing the Cartesian product of the input tubes + */ template SlicedTube tube_cart_prod(const X&... x) { + static_assert(sizeof...(X) > 0, "tube_cart_prod requires at least one tube"); Index s = 0; ((s += x.size()), ...); - auto tdomain = std::get<0>(std::make_tuple(x...)).tdomain(); + const auto& x0 = std::get<0>(std::forward_as_tuple(x...)); + auto tdomain = x0.tdomain(); + assert_release(((x.tdomain() == tdomain) && ...)); SlicedTube v(tdomain, IntervalVector(s)); for(auto it = tdomain->begin() ; it != tdomain->end() ; it++) diff --git a/src/core/functions/analytic/codac2_AnalyticFunction.h b/src/core/functions/analytic/codac2_AnalyticFunction.h index 1985f8358..337a64d03 100644 --- a/src/core/functions/analytic/codac2_AnalyticFunction.h +++ b/src/core/functions/analytic/codac2_AnalyticFunction.h @@ -256,7 +256,7 @@ namespace codac2 os << "("; for(size_t i = 0 ; i < f.args().size() ; i++) os << (i!=0 ? "," : "") << f.args()[i]->name(); - os << ") ↦ " << f.expr()->str(); + os << ") -> " << f.expr()->str(); return os; } diff --git a/src/core/matrices/eigen/MatrixBase_addons/codac2_MatrixBase_addons_IntervalMatrixBase.h b/src/core/matrices/eigen/MatrixBase_addons/codac2_MatrixBase_addons_IntervalMatrixBase.h index c894cc438..187c91da9 100644 --- a/src/core/matrices/eigen/MatrixBase_addons/codac2_MatrixBase_addons_IntervalMatrixBase.h +++ b/src/core/matrices/eigen/MatrixBase_addons/codac2_MatrixBase_addons_IntervalMatrixBase.h @@ -216,67 +216,73 @@ inline auto diam() const /** * \brief Returns the minimum radius among the interval elements. * + * \param among_indices Optional restricted set of dimension indices. * \return The smallest radius value in the matrix. */ -inline double min_rad() const +inline double min_rad(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return coeff(this->extr_diam_index(true)).rad(); + return coeff(this->extr_diam_index(true,among_indices)).rad(); } /** * \brief Returns the maximum radius among the interval elements. * + * \param among_indices Optional restricted set of dimension indices. * \return The largest radius value in the matrix. */ -inline double max_rad() const +inline double max_rad(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return coeff(this->extr_diam_index(false)).rad(); + return coeff(this->extr_diam_index(false,among_indices)).rad(); } /** * \brief Returns the minimum diameter among the interval elements. * + * \param among_indices Optional restricted set of dimension indices. * \return The smallest diameter value in the matrix. */ -inline double min_diam() const +inline double min_diam(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return coeff(this->extr_diam_index(true)).diam(); + return coeff(this->extr_diam_index(true,among_indices)).diam(); } /** * \brief Returns the maximum diameter among the interval elements. * + * \param among_indices Optional restricted set of dimension indices. * \return The largest diameter value in the matrix. */ -inline double max_diam() const +inline double max_diam(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return coeff(this->extr_diam_index(false)).diam(); + return coeff(this->extr_diam_index(false,among_indices)).diam(); } /** * \brief Returns the index of the element with the minimum diameter. * + * \param among_indices Optional restricted set of dimension indices. * \return The index of the matrix element with the smallest diameter. */ -inline Index min_diam_index() const +inline Index min_diam_index(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return this->extr_diam_index(true); + return this->extr_diam_index(true,among_indices); } /** * \brief Returns the index of the element with the maximum diameter. * + * \param among_indices Optional restricted set of dimension indices. * \return The index of the matrix element with the largest diameter. */ -inline Index max_diam_index() const +inline Index max_diam_index(const std::vector& among_indices = {}) const requires IsIntervalDomain { - return this->extr_diam_index(false); + return this->extr_diam_index(false,among_indices); } /** @@ -290,14 +296,20 @@ inline Index max_diam_index() const * - If looking for maximum diameter, unbounded intervals are considered. * * \param min If ``true``, returns the index of the element with minimum diameter; otherwise maximum diameter. + * \param among_indices Optional restricted set of dimension indices. * * \return Index of the element with the minimum or maximum diameter. * * \pre The matrix must not be empty (diameter undefined otherwise). */ -inline Index extr_diam_index(bool min) const +inline Index extr_diam_index(bool min, const std::vector& among_indices = {}) const requires IsIntervalDomain { + assert_release( + among_indices.empty() || + std::ranges::all_of(among_indices, [&](Index k){ return 0 <= k && k < this->size(); }) + ); + // This code originates from the ibex-lib // See: ibex_TemplateVector.h // Author: Gilles Chabert @@ -311,6 +323,10 @@ inline Index extr_diam_index(bool min) const for(i = 0 ; i < this->size() ; i++) { + if(!among_indices.empty() + && std::find(among_indices.begin(),among_indices.end(),i) == among_indices.end()) + continue; // avoiding this index + if(coeff(i).is_unbounded()) { unbounded = true; @@ -330,8 +346,14 @@ inline Index extr_diam_index(bool min) const if(min && selected_index == -1) { assert(unbounded); - // the selected interval is the first one. - i = 0; + + if(among_indices.empty()) + { + // the selected interval is the first one. + i = 0; + } + else + i = among_indices[0]; } // The unbounded intervals are not considered if we look for the minimal diameter @@ -343,6 +365,10 @@ inline Index extr_diam_index(bool min) const for(; i < this->size() ; i++) { + if(!among_indices.empty() + && std::find(among_indices.begin(),among_indices.end(),i) == among_indices.end()) + continue; // avoiding this index + if(coeff(i).lb() == -codac2::oo) { if(coeff(i).ub() == codac2::oo) diff --git a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h index b9c08c129..64ef607a3 100644 --- a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h +++ b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h @@ -331,12 +331,15 @@ inline auto bisect(Index i, float ratio = 0.49) const * Uses ``max_diam_index()`` to find the element with the largest diameter and bisects * it using the given ratio. * + * The indices may be restricted to a set of values provided in ``among_indices``. + * * \param ratio Ratio to determine the split point within the interval (default 0.49). + * \param among_indices Optional set of indices among which the bisection will be performed. * \return A pair of matrices resulting from the bisection of the largest diameter interval. */ template requires IsIntervalDomain -inline auto bisect_largest(float ratio = 0.49) const +inline auto bisect_largest(double ratio = 0.49, const std::vector& among_indices = {}) const { - return bisect(this->max_diam_index(), ratio); + return bisect(this->max_diam_index(among_indices), ratio); } \ No newline at end of file diff --git a/src/core/tools/codac2_Index.h b/src/core/tools/codac2_Index.h index 874b08182..cd3068f25 100644 --- a/src/core/tools/codac2_Index.h +++ b/src/core/tools/codac2_Index.h @@ -8,9 +8,10 @@ */ #pragma once +#include namespace codac2 { // The Index type is the same as for Eigen - typedef long int Index; + using Index = std::ptrdiff_t; } \ No newline at end of file diff --git a/src/core/trajectory/codac2_AnalyticTraj.h b/src/core/trajectory/codac2_AnalyticTraj.h index 37b9944cd..8c5cf51cd 100644 --- a/src/core/trajectory/codac2_AnalyticTraj.h +++ b/src/core/trajectory/codac2_AnalyticTraj.h @@ -22,7 +22,7 @@ namespace codac2 using Type = T; - AnalyticTraj(const AnalyticFunction& f, const Interval& tdomain) + AnalyticTraj(const Interval& tdomain, const AnalyticFunction& f) : TrajBase(), AnalyticFunction(f), _tdomain(tdomain) { assert_release(f.args().total_size() == 1 && "domain of f must be 1d"); @@ -89,3 +89,5 @@ namespace codac2 Interval _tdomain; }; } + +#include "codac2_TrajBase_impl.h" \ No newline at end of file diff --git a/src/core/trajectory/codac2_SampledTraj.h b/src/core/trajectory/codac2_SampledTraj.h index ba247b3c7..3aaaa0537 100644 --- a/src/core/trajectory/codac2_SampledTraj.h +++ b/src/core/trajectory/codac2_SampledTraj.h @@ -402,7 +402,7 @@ namespace codac2 template inline std::ostream& operator<<(std::ostream& os, const SampledTraj& x) { - os << "SampledTraj. " << x.tdomain() << "↦"; + os << "SampledTraj. " << x.tdomain() << "->"; if constexpr(std::is_same_v) { os << "["; @@ -453,3 +453,5 @@ namespace codac2 return v; } } + +#include "codac2_TrajBase_impl.h" \ No newline at end of file diff --git a/src/core/trajectory/codac2_TrajBase.h b/src/core/trajectory/codac2_TrajBase.h index b72d54faa..46e12f645 100644 --- a/src/core/trajectory/codac2_TrajBase.h +++ b/src/core/trajectory/codac2_TrajBase.h @@ -36,69 +36,11 @@ namespace codac2 virtual T operator()(double t) const = 0; virtual typename Wrapper::Domain operator()(const Interval& t) const = 0; - auto nan_value() const - { - if constexpr(std::is_same_v || std::is_same_v::Type,ScalarType>) - return std::numeric_limits::quiet_NaN(); - - else - return T((*this)(tdomain().lb())) // we obtain the output dimension by an evalution... - .init(std::numeric_limits::quiet_NaN()); - } - - virtual SampledTraj sampled(double dt) const - { - assert_release(dt > 0.); - assert_release(!is_empty()); - - auto tdom = tdomain(); - SampledTraj straj; - for(double t = tdom.lb() ; t < tdom.ub() ; t+=dt) - straj.set((*this)(t), t); - straj.set((*this)(tdom.ub()), tdom.ub()); - return straj; - } - + // Implementation in codac2_TrajBase_impl.h + auto nan_value() const; + virtual SampledTraj sampled(double dt) const; template - SampledTraj sampled_as(const SampledTraj& x) const - { - assert_release(x.tdomain().is_subset(this->tdomain())); - - SampledTraj straj; - for(const auto& [ti,dump] : x) - straj.set((*this)(ti), ti); - return straj; - } - - SampledTraj primitive(double dt) const - { - assert_release(dt > 0.); - assert_release(!is_empty()); - - T s = [this]() { - if constexpr(std::is_same_v) - return 0.; - else - return T((*this)(this->tdomain().lb())).init(0.); - }(); - - SampledTraj p; - double t = tdomain().lb(), last_t = t; - p.set(s, t); t += dt; - - while(t < tdomain().ub()) - { - s += ((*this)(last_t)+(*this)(t))*dt/2.; - p.set(s, t); - last_t = t; - t += dt; - } - - t = tdomain().ub(); - s += ((*this)(last_t)+(*this)(t))*(t-last_t)/2.; - p.set(s, t); - - return p; - } + SampledTraj sampled_as(const SampledTraj& x) const; + SampledTraj primitive(double dt) const; }; -} +} \ No newline at end of file diff --git a/src/core/trajectory/codac2_TrajBase_impl.h b/src/core/trajectory/codac2_TrajBase_impl.h new file mode 100644 index 000000000..50d123e78 --- /dev/null +++ b/src/core/trajectory/codac2_TrajBase_impl.h @@ -0,0 +1,82 @@ +/** + * \file codac2_TrajBase_impl.h + * ---------------------------------------------------------------------------- + * \date 2024 + * \author Simon Rohou + * \copyright Copyright 2024 Codac Team + * \license GNU Lesser General Public License (LGPL) + */ + +#pragma once + +namespace codac2 +{ + template + auto TrajBase::nan_value() const + { + if constexpr(std::is_same_v || std::is_same_v::Type,ScalarType>) + return std::numeric_limits::quiet_NaN(); + + else + return T((*this)(tdomain().lb())) // we obtain the output dimension by an evalution... + .init(std::numeric_limits::quiet_NaN()); + } + + template + SampledTraj TrajBase::sampled(double dt) const + { + assert_release(dt > 0.); + assert_release(!is_empty()); + + auto tdom = tdomain(); + SampledTraj straj; + for(double t = tdom.lb() ; t < tdom.ub() ; t+=dt) + straj.set((*this)(t), t); + straj.set((*this)(tdom.ub()), tdom.ub()); + return straj; + } + + template + template + SampledTraj TrajBase::sampled_as(const SampledTraj& x) const + { + assert_release(x.tdomain().is_subset(this->tdomain())); + + SampledTraj straj; + for(const auto& [ti,dump] : x) + straj.set((*this)(ti), ti); + return straj; + } + + template + SampledTraj TrajBase::primitive(double dt) const + { + assert_release(dt > 0.); + assert_release(!is_empty()); + + T s = [this]() { + if constexpr(std::is_same_v) + return 0.; + else + return T((*this)(this->tdomain().lb())).init(0.); + }(); + + SampledTraj p; + double t = tdomain().lb(), last_t = t; + p.set(s, t); t += dt; + + while(t < tdomain().ub()) + { + s += ((*this)(last_t)+(*this)(t))*dt/2.; + p.set(s, t); + last_t = t; + t += dt; + } + + t = tdomain().ub(); + s += ((*this)(last_t)+(*this)(t))*(t-last_t)/2.; + p.set(s, t); + + return p; + } +} diff --git a/src/graphics/figures/codac2_Figure2D.cpp b/src/graphics/figures/codac2_Figure2D.cpp index 0543c0b31..a1c2c5a2d 100644 --- a/src/graphics/figures/codac2_Figure2D.cpp +++ b/src/graphics/figures/codac2_Figure2D.cpp @@ -434,6 +434,11 @@ void Figure2D::plot_trajectory(const SampledTraj& x, const StyleProperti display_and_clear(); } +void Figure2D::plot_trajectory(const AnalyticTraj& x, const StyleProperties& style) +{ + plot_trajectory(x.sampled(_axes[0].limits.diam()/1e4), style); +} + void Figure2D::plot_trajectories(const SampledTraj& x) { for(const auto& xi : as_scalar_trajs(x)) diff --git a/src/graphics/figures/codac2_Figure2D.h b/src/graphics/figures/codac2_Figure2D.h index e55bcbbe7..22c6cc37e 100644 --- a/src/graphics/figures/codac2_Figure2D.h +++ b/src/graphics/figures/codac2_Figure2D.h @@ -378,6 +378,14 @@ namespace codac2 */ void plot_trajectory(const SampledTraj& x, const StyleProperties& style = StyleProperties()); + /** + * \brief Plots a trajectory on the figure (x-axis is the time) + * + * \param x AnalyticTraj to plot + * \param style Style of the trajectory (edge color) + */ + void plot_trajectory(const AnalyticTraj& x, const StyleProperties& style = StyleProperties()); + /** * \brief Plots a set of trajectories on the figure (x-axis is the time) with random colors * @@ -941,6 +949,18 @@ namespace codac2 selected_fig()->plot_trajectory(x,style); } + /** + * \brief Plots a trajectory on the figure (x-axis is the time) + * + * \param x AnalyticTraj to plot + * \param style Style of the trajectory (edge color) + */ + static void plot_trajectory(const AnalyticTraj& x, const StyleProperties& style = StyleProperties()) + { + auto_init(); + selected_fig()->plot_trajectory(x,style); + } + /** * \brief Plots a set of trajectories on the figure (x-axis is the time) * diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c9ef12a5b..c38934659 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,6 +53,7 @@ list(APPEND SRC_TESTS # listing files without extension core/domains/interval/codac2_tests_Interval_operations 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/tube/codac2_tests_TDomain @@ -60,7 +61,7 @@ list(APPEND SRC_TESTS # listing files without extension core/domains/tube/codac2_tests_Slice_polygon core/domains/tube/codac2_tests_SlicedTube core/domains/tube/codac2_tests_SlicedTube_integral - ../doc/manual/manual/intervals/src + ../doc/manual/manual/tubes/src core/functions/analytic/codac2_tests_AnalyticFunction ../doc/manual/manual/functions/analytic/src diff --git a/tests/core/domains/tube/codac2_tests_SlicedTube.cpp b/tests/core/domains/tube/codac2_tests_SlicedTube.cpp index 9d42d3ef7..686e4a670 100644 --- a/tests/core/domains/tube/codac2_tests_SlicedTube.cpp +++ b/tests/core/domains/tube/codac2_tests_SlicedTube.cpp @@ -417,7 +417,7 @@ TEST_CASE("SlicedTube") { ScalarVar t; AnalyticFunction f { {t}, cos(t) }; - AnalyticTraj analytic_traj(f, {-PI,PI}); + AnalyticTraj analytic_traj({-PI,PI},f); auto sampled_traj = analytic_traj.sampled(1e-2); auto tdomain = create_tdomain({-PI,PI},1e-2,false); SlicedTube tube(tdomain, sampled_traj); @@ -437,7 +437,7 @@ TEST_CASE("SlicedTube") vec(2*cos(t),sin(2*t)) }; - auto analytic_traj = AnalyticTraj(f, {0,5}); + auto analytic_traj = AnalyticTraj({0,5},f); auto sampled_traj = analytic_traj.sampled(1e-2); auto tdomain = create_tdomain({0,5},1e-3,false); SlicedTube tube(tdomain, sampled_traj); diff --git a/tests/core/domains/tube/codac2_tests_SlicedTube.py b/tests/core/domains/tube/codac2_tests_SlicedTube.py index 9d7766e34..8b0e39a51 100644 --- a/tests/core/domains/tube/codac2_tests_SlicedTube.py +++ b/tests/core/domains/tube/codac2_tests_SlicedTube.py @@ -321,7 +321,7 @@ def test_SlicedTube_as_operator_1dcase(self): t = ScalarVar() f = AnalyticFunction([t], cos(t)) - analytic_traj = AnalyticTraj(f, [-PI,PI]) + analytic_traj = AnalyticTraj([-PI,PI],f) sampled_traj = analytic_traj.sampled(1e-2) tdomain = create_tdomain([-PI,PI],1e-2,False) tube = SlicedTube(tdomain, sampled_traj) @@ -342,7 +342,7 @@ def test_SlicedTube_as_operator_ndcase(self): vec(2*cos(t),sin(2*t)) ) - analytic_traj = AnalyticTraj(f, [0,5]) + analytic_traj = AnalyticTraj([0,5],f) sampled_traj = analytic_traj.sampled(1e-2) tdomain = create_tdomain([0,5],1e-3,False) tube = SlicedTube(tdomain, sampled_traj) diff --git a/tests/core/tools/codac2_tests_transformations.cpp b/tests/core/tools/codac2_tests_transformations.cpp index 9a182c0f4..ba4ed42bb 100644 --- a/tests/core/tools/codac2_tests_transformations.cpp +++ b/tests/core/tools/codac2_tests_transformations.cpp @@ -27,7 +27,7 @@ TEST_CASE("Affine transformation") 2*sin(t)+0.1*sin(10*t) }); - SampledTraj src = AnalyticTraj(f_src,{-1,3}).sampled(0.01); + SampledTraj src = AnalyticTraj({-1,3},f_src).sampled(0.01); // The dst trajectory is obtained analytically with a // transformation described by the parameters: @@ -43,7 +43,7 @@ TEST_CASE("Affine transformation") b*sin(a)*f_src(t)[0]+b*cos(a)*f_src(t)[1] + T[1] + 0.05*sin(100*t) }); - SampledTraj dst = AnalyticTraj(f_dst,{-1,3}).sampled(0.01); + SampledTraj dst = AnalyticTraj({-1,3},f_dst).sampled(0.01); //DefaultFigure::set_window_properties({75,75},{700,700}); //DefaultFigure::set_axes(axis(0,{-8,4}), axis(1,{-4,8})); diff --git a/tests/core/tools/codac2_tests_transformations.py b/tests/core/tools/codac2_tests_transformations.py index 2a1caeb43..6344e1625 100644 --- a/tests/core/tools/codac2_tests_transformations.py +++ b/tests/core/tools/codac2_tests_transformations.py @@ -23,7 +23,7 @@ def test_affine_transformation(self): 2*sin(t)+0.1*sin(10*t) ]) - src = AnalyticTraj(f_src,[-1,3]).sampled(0.01) + src = AnalyticTraj([-1,3],f_src).sampled(0.01) # The dst trajectory is obtained analytically with a # transformation described by the parameters: @@ -36,7 +36,7 @@ def test_affine_transformation(self): b*sin(a)*f_src(t)[0]+b*cos(a)*f_src(t)[1] + T[1] + 0.05*sin(100*t) ]) - dst = AnalyticTraj(f_dst,[-1,3]).sampled(0.01) + dst = AnalyticTraj([-1,3],f_dst).sampled(0.01) # Computing the transformation diff --git a/tests/core/trajectory/codac2_tests_AnalyticTraj.cpp b/tests/core/trajectory/codac2_tests_AnalyticTraj.cpp index ff91cb916..cd85b01bd 100644 --- a/tests/core/trajectory/codac2_tests_AnalyticTraj.cpp +++ b/tests/core/trajectory/codac2_tests_AnalyticTraj.cpp @@ -28,7 +28,7 @@ TEST_CASE("AnalyticTraj") sqr(t) }; - AnalyticTraj traj(f, {-1,10}); + AnalyticTraj traj({-1,10},f); CHECK(traj.tdomain() == Interval(-1,10)); CHECK(traj.codomain() == Interval(0,100)); diff --git a/tests/core/trajectory/codac2_tests_AnalyticTraj.py b/tests/core/trajectory/codac2_tests_AnalyticTraj.py index 99eccdcff..664d37e2e 100644 --- a/tests/core/trajectory/codac2_tests_AnalyticTraj.py +++ b/tests/core/trajectory/codac2_tests_AnalyticTraj.py @@ -25,7 +25,7 @@ def test_AnalyticTraj(self): sqr(t) ) - traj = AnalyticTraj(f, [-1,10]) + traj = AnalyticTraj([-1,10],f) self.assertTrue(traj.tdomain() == Interval(-1,10)) self.assertTrue(traj.codomain() == Interval(0,100)) diff --git a/tests/core/trajectory/codac2_tests_SampledTraj.cpp b/tests/core/trajectory/codac2_tests_SampledTraj.cpp index 7da03d89a..4addc7c93 100644 --- a/tests/core/trajectory/codac2_tests_SampledTraj.cpp +++ b/tests/core/trajectory/codac2_tests_SampledTraj.cpp @@ -68,7 +68,7 @@ TEST_CASE("SampledTraj as operator (1d case)") { ScalarVar t; AnalyticFunction f { {t}, cos(t) }; - AnalyticTraj analytic_traj(f, {-PI,PI}); + AnalyticTraj analytic_traj({-PI,PI},f); auto sampled_traj = analytic_traj.sampled(1e-2); auto g = sampled_traj.as_function(); @@ -86,7 +86,7 @@ TEST_CASE("SampledTraj as operator (nd case)") vec(2*cos(t),sin(2*t)) }; - auto analytic_traj = AnalyticTraj(f, {0,5}); + auto analytic_traj = AnalyticTraj({0,5},f); auto sampled_traj = analytic_traj.sampled(1e-2); auto g = sampled_traj.as_function(); @@ -114,7 +114,7 @@ TEST_CASE("SampledTraj: operations") { ScalarVar t; AnalyticFunction h { {t}, t }; - auto analytic_traj = AnalyticTraj(h, {-PI,PI}); + auto analytic_traj = AnalyticTraj({-PI,PI},h); SampledTraj x = analytic_traj.sampled(1e-2); CHECK(Approx(cos(x).codomain(),1e-5) == Interval(-1,1)); x = cos(x) + 4.; @@ -136,8 +136,8 @@ TEST_CASE("SampledTraj: derivative") { ScalarVar t; AnalyticFunction f({t}, sqr(t)*exp(sin(t))); - SampledTraj x = AnalyticTraj(f,{0,10}).sampled(1e-3); - SampledTraj s = AnalyticTraj(AnalyticFunction({t},exp(sin(t))*(2*t+sqr(t)*cos(t))),{0,10}).sampled(1e-2); + SampledTraj x = AnalyticTraj({0,10},f).sampled(1e-3); + SampledTraj s = AnalyticTraj({0,10},AnalyticFunction({t},exp(sin(t))*(2*t+sqr(t)*cos(t)))).sampled(1e-2); //DefaultFigure::plot_trajectory(x); //DefaultFigure::plot_trajectory(x.derivative(), Color::blue()); diff --git a/tests/core/trajectory/codac2_tests_SampledTraj.py b/tests/core/trajectory/codac2_tests_SampledTraj.py index c97be21ab..da0072f37 100644 --- a/tests/core/trajectory/codac2_tests_SampledTraj.py +++ b/tests/core/trajectory/codac2_tests_SampledTraj.py @@ -64,7 +64,7 @@ def test_SampledTraj(self): f = AnalyticFunction( [t], cos(t) ) - analytic_traj = AnalyticTraj(f, [-math.pi,math.pi]) + analytic_traj = AnalyticTraj([-math.pi,math.pi],f) sampled_traj = analytic_traj.sampled(1e-2) g = sampled_traj.as_function() @@ -85,7 +85,7 @@ def test_SampledTraj(self): vec(2*cos(t),sin(2*t)) ) - analytic_traj = AnalyticTraj(f, [0,5]) + analytic_traj = AnalyticTraj([0,5],f) sampled_traj = analytic_traj.sampled(1e-2) g = sampled_traj.as_function() @@ -122,8 +122,8 @@ def test_SampledTraj(self): t = ScalarVar() f = AnalyticFunction([t], sqr(t)*exp(sin(t))) - x = AnalyticTraj(f,[0,10]).sampled(1e-3) - s = AnalyticTraj(AnalyticFunction([t],exp(sin(t))*(2*t+sqr(t)*cos(t))),[0,10]).sampled(1e-2) + x = AnalyticTraj([0,10],f).sampled(1e-3) + s = AnalyticTraj([0,10],AnalyticFunction([t],exp(sin(t))*(2*t+sqr(t)*cos(t)))).sampled(1e-2) d = x.derivative() p = d.primitive()