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) [](https://github.com/codac-team/codac/actions)
+# [Codac: constraint-programming for robotics](http://codac.io) [](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