We now focus on dynamical items that are evolving with time.
The trajectories \(x(\cdot)\) and vector of trajectories \(\mathbf{x}(\cdot)\), presented in the previous pages, have also their interval counterpart: tubes. This page provides the main uses of these sets. They will be involved afterwards for solving problems related to differential equations.
A tube is defined over a temporal t-domain \([t_0,t_f]\) as an envelope of trajectories that are defined over the same t-domain. We speak about an envelope as it may exist trajectories enclosed in the tube that are not solutions of our problem.
In this library, a tube \([x](\cdot):[t_0,t_f]\rightarrow\mathbb{IR}\) is an interval of two trajectories \([\underline{x}(\cdot),\overline{x}(\cdot)]\) such that \(\forall t\in[t_0,t_f]\), \(\underline{x}(t)\leqslant\overline{x}(t)\). We also consider empty tubes that depict an absence of solutions, denoted \(\varnothing(\cdot)\).
A trajectory \(x(\cdot)\) belongs to the tube \(\left[x\right](\cdot)\) if \(\forall t\in[t_0,t_f], x\left(t\right)\in\left[x\right]\left(t\right)\).
Fig. 8 Illustration a one-dimensional tube enclosing a trajectory \(x^*(\cdot)\) plotted in orange. The figure shows two interval evaluations: \([x]([t_1])\) and \([x](t_2)\).
Note
Important: we assume that all the tubes and/or the trajectories involved in a given resolution process share the same t-domain \([t_0,t_f]\).
In Codac, tubes are implemented as lists of slices.
More precisely, a tube \([x](\cdot)\) with a sampling time \(\delta>0\) is implemented as a box-valued function which is constant for all \(t\) inside intervals \([k\delta,k\delta+\delta]\), \(k\in\mathbb{N}\).
The box \([k\delta,k\delta+\delta]\times\left[x\right]\left(t_{k}\right)\), with \(t_{k}\in[k\delta,k\delta+\delta]\), is called the \(k\)-th slice of the tube \([x](\cdot)\) and is denoted by \([\![x]\!]^{(k)}\).
Fig. 9 A tube \([x](\cdot)\) represented by a set of \(\delta\)-width slices. This implementation can be used to enclose signals such as \(x^*(\cdot)\).
Added in version 2.0: Custom discretization of tubes is now at hand, they can be made of slices that are not all defined with the same sampling time \(\delta\).
This implementation takes rigorously into account floating point precision when building a tube.
Further computations involving \([x](\cdot)\) will be implicitly based on its slices, thus keeping a reliable outer approximation of the solution set.
The dt variable defines the temporal width of the slices. Note that it is also possible to create slices of different width; this will be explained afterwards.
To create a copy of a tube with the same time discretization, use:
x5=Tube(x4)# identical tube (100 slices, [0,10]→[0,∞])x5.set(Interval(5))# 100 slices, same timestep, but [0,10]→[5]
Tubex5(x4);// identical tube (100 slices, [0,10]→[0,∞])x5.set(Interval(5.));// 100 slices, same timestep, but [0,10]→[5]
As tubes are intervals of trajectories, a Tube can be defined from Trajectory objects:
traj=TrajectoryVector(tdomain,TFunction("(sin(t) ; cos(t) ; cos(t)+t/10)"))x8=Tube(traj[0],dt)# 100 slices tube enclosing sin(t)x9=Tube(traj[1],traj[2],dt)# 100 slices tube defined as [cos(t),cos(t)+t/10]
TrajectoryVectortraj(tdomain,TFunction("(sin(t) ; cos(t) ; cos(t)+t/10)"));Tubex8(traj[0],dt);// 100 slices tube enclosing sin(t)Tubex9(traj[1],traj[2],dt);// 100 slices tube defined as [cos(t),cos(t)+t/10]
Fig. 10 Result of tubes \([x_8](t)=[\sin(t),\sin(t)]\), \([x_9](t)=[\cos(t),\cos(t)+\frac{t}{10}]\), made of 100 slices.
It is also possible to create a tube from a thick function, where the uncertainty is explicitly set in the formula:
Fig. 11 Result of tube \([x_{10}](\cdot)\) made of 1000 slices.
Finally, as tube is an envelope (union) of trajectories, the following operations are allowed:
f=TFunction("(cos(t) ; cos(t)+t/10 ; sin(t)+t/10 ; sin(t))")# 4d temporal functiontraj=TrajectoryVector(tdomain,f)# 4d trajectory defined over [0,10]# 1d tube [x](·) defined as a union of the 4 trajectoriesx=Tube(traj[0],dt)|traj[1]|traj[2]|traj[3]
TFunctionf("(cos(t) ; cos(t)+t/10 ; sin(t)+t/10 ; sin(t))");// 4d temporal functionTrajectoryVectortraj(tdomain,f);// 4d trajectory defined over [0,10]// 1d tube [x](·) defined as a union of the 4 trajectoriesTubex=Tube(traj[0],dt)|traj[1]|traj[2]|traj[3];
The extension to the vector case is the class TubeVector, allowing to create tubes \([\mathbf{x}](\cdot):[t_0,t_f]\to\mathbb{IR}^n\).
The following example
# TubeVector from a formula; the function's output is two-dimensionalx=TubeVector(tdomain,dt, \
TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; \ cos(t)+sin(t/0.2)*[-0.1,0.1])"))
// TubeVector from a formula; the function's output is two-dimensionalTubeVectorx(tdomain,dt,TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]) ; \ cos(t)+sin(t/0.2)*[-0.1,0.1])"));
produces (each dimension displayed on the same figure):
Added in version 3.0.10: The definition of a TubeVector can also be done from a list. The above example could be written as:
# TubeVector as a list of scalar tubesx=TubeVector([ \
Tube(tdomain,dt,TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]))")), \
Tube(tdomain,dt,TFunction("cos(t)+sin(t/0.2)*[-0.1,0.1]")) \
])
// TubeVector as a list of scalar tubesTubeVectorx({Tube(tdomain,dt,TFunction("(sin(sqrt(t)+((t-5)^2)*[-0.01,0.01]))")),Tube(tdomain,dt,TFunction("cos(t)+sin(t/0.2)*[-0.1,0.1]"))});
As for Vector and IntervalVector objects, a Tube component of a TubeVector is available by reference with [].
Vector operations are also at hand:
Once created, several evaluations of the tubes can be made, as for trajectories.
Accessing values with ()
x(6.)# evaluation of [x](·) at 6x(Interval(5,6))# evaluation of [x](·) over [5,6]x.codomain()# envelope of values
x(6.)// evaluation of [x](·) at 6x(Interval(5.,6.))// evaluation of [x](·) over [5,6]x.codomain()// envelope of values
Inversion of tubes
The inversion of a tube \([x](\cdot)\), denoted \([x]^{-1}([y])\), returns the set \([t]\) enclosing the preimages of \([y]\). The invert() method returns the squared-union of these subsets, or the set of solutions within a vector of Interval objects. The following example returns the different subsets of the inversion \([x]^{-1}([0,0.2])\) projected in red in next figure:
v_t=[]x.invert(Interval(0,0.2),v_t,tdomain)# inversion# tdomain is a temporal subset over which the inversion is performedfortinv_t:tbox=IntervalVector([t,[0,0.2]])fig.draw_box(tbox,"red")# boxes display
vector<Interval>v_t;// vector of preimagesx.invert(Interval(0.,0.2),v_t,tdomain);// inversion// tdomain is a temporal subset over which the inversion is performedfor(auto&t:v_t){IntervalVectortbox={t,{0.,0.2}};fig.draw_box(tbox,"red");// boxes display}
Set operations are available for Tube and TubeVector objects. In the following table, if \([x](\cdot)\) is a tube object, \(z(\cdot)\) is a trajectory.
Testing if a tube \([x](\cdot)\) contains a solution \(z(\cdot)\) may lead to uncertainties. Indeed, the reliable representation of a Trajectory may lead to some wrapping effect, and so this contains test may not be able to conclude. Therefore, the contains() method returns a BoolInterval value. Its values can be either YES, NO or MAYBE. The MAYBE case is rare but may appear due to the numerical representation of a trajectory. In practice, this may happen if the thin envelope of \(z(\cdot)\) overlaps a boundary of the tube \([x](\cdot)\).
In addition of these test functions, operations on sets are available:
Code
Meaning, \(\forall t\in[t_0,t_f]\)
x&y
\([x](t)\cap [y](t)\)
x|y
\([x](t)\sqcup[y](t)\)
x.set_empty()
\([x](t)\leftarrow \varnothing\)
x=y
\([x](t)\leftarrow [y](t)\)
x&=y
\([x](t)\leftarrow ([x](t)\cap [y](t))\)
x|=y
\([x](t)\leftarrow ([x](t)\sqcup[y](t))\)
Finally, one can also access properties of the sets. First for Tube:
Return type
Code
Meaning
Trajectory
x.diam()
diameters of the tube as a trajectory,
\(d(\cdot)=\overline{x}(\cdot)-\underline{x}(\cdot)\)
double
x.max_diam()
maximal diameter
double
x.volume()
the volume (surface) of the tube
Tube
x.inflate(eps)
a Tube with the same midtraj and radius increased by eps
Then for TubeVector:
Return type
Code
Meaning
TrajectoryVector
x.diam()
diameters of the tube as a trajectory,
\(\mathbf{d}(\cdot)=\overline{\mathbf{x}}(\cdot)-\underline{\mathbf{x}}(\cdot)\)
Vector
x.max_diam()
maximal diameter
Trajectory
x.diag()
approximated trajectory: list of diagonals of each slice
double
x.volume()
the volume of the tube
TubeVector
x.inflate(eps)
new tube: same midtraj, each dimension increased by eps
The diam() methods
These methods are used to evaluate the thickness of a Tube or a TubeVector. They are mainly used for display purposes, for instance for displaying a tube with a color function depending on its thickness.
However, without derivative knowledge, and because the tube is made of boxed slices, the trajectory will be discontinuous and so the returned object will not reliably represent the actual diameters.
The diag() methods
It holds the same of the diag() methods.
Classical operations on sets are applicable on tubes.
We recall that the tubes and trajectories have to share the same t-domain for these operations.
Reliable integral computations are available on tubes.
Fig. 12 Hatched part depicts the lower bound of \(\displaystyle\int_{a}^{b}[x](\tau)d\tau\).
The computation is reliable because it stands on the tube’s slices. The result is an outer approximation of the integral of the tube represented by these slices:
Fig. 13 Outer approximation of the lower bound of \(\int_{a}^{b}[x](\tau)d\tau\).
Computation of the tube primitive \([p](\cdot)=\int_{0}^{\cdot}[x](\tau)d\tau\):
p=x.primitive()
Tubep=x.primitive();
Computation of the interval-integral \([s]=\int_{0}^{[t]}[x](\tau)d\tau\):
t=Interval(...)s=x.integral(t)
Intervalt(...);Intervals=x.integral(t);
Computation of \([s]=\int_{[t_1]}^{[t_2]}[x](\tau)d\tau\):
Also, a decomposition of the interval-integral of \([x](\cdot)=[x^-(\cdot),x^+(\cdot)]\) with \([s^-]=\int_{[t_1]}^{[t_2]}x^-(\tau)d\tau\) and \([s^+]=\int_{[t_1]}^{[t_2]}x^+(\tau)d\tau\) is computable by:
t1=Interval(...)t2=Interval(...)s=x.partial_integral(t1,t2)# s[0] is [s^-]# s[1] is [s^+]
Intervalt1,t2;pair<Interval,Interval>s;s=x.partial_integral(t1,t2);// s.first is [s^-]// s.second is [s^+]
The set() methods allow various updates on tubes. For instance:
x.set(Interval(0,2),Interval(5,6))# then [x]([5,6])=[0,2]
x.set(Interval(0.,2.),Interval(5.,6.));// then [x]([5,6])=[0,2]
produces:
Warning
Be careful when updating a tube without the use of dedicated contractors. Tube discretization has to be kept in mind whenever an update is performed for some input \(t\). For reliable operations, please see the contractors section.
See also the following methods:
x.set(Interval(0,oo))# set a constant codomain for all tx.set(Interval(0),4.)# set a value at some t: [x](4)=[0]x.set_empty()# empty set for all t
x.set(Interval(0,oo));// set a constant codomain for all tx.set(Interval(0.),4.);// set a value at some t: [x](4)=[0]x.set_empty();// empty set for all t
The following methods are related to t-domains and slices structure:
Return type
Code
Meaning
Interval
x.tdomain()
temporal domain \([t_0,t_f]\) (t-domain)
–
x.shift_tdomain(a)
shifts the t-domain to \([t_0+a,t_f+a]\)
bool
same_slicing(x,y)
tests if x and y have the same slicing (static)
–
x.sample(t)
samples the tube at \(t\) (adds a slice)
–
x.sample(y)
samples x so that it shares the sampling of tube y
–
x.remove_gate(t)
removes the sampling at \(t\), if it exists
The sample() methods
Custom sampling of tubes is available. The sample(t) method allows to cut a slice defined over \(t\) into two slices on each side of \(t\). If \(t\) already separates two slices (if it corresponds to a gate), then nothing is changed.
Next pages will present reliable operators to reduce the range of the presented domains (intervals, boxes, tubes) in a reliable way and according to the constraints defining a problem.