PEIBOS-CAPD
When compiling CODAC with the codac-capd extension (see here), the CAPD version of the PEIBOS library is also compiled.
Let us consider an initial set \(\mathbb{X}_0 \subset \mathbb{R}^n\) with its boundary \(\partial \mathbb{X}_0\). Considering a dynamical system \(\dot{\mathbf{x}}=\gamma(\mathbf{x})\), the PEIBOS tool allows to compute the reach set \(\mathbf{X}_t=\left\{ \mathbf{x}(t) \mid \mathbf{x} \in \partial \mathbb{X}_0 \right\}\).
Gnomonic atlas
To handle the boundary of the initial set \(\mathbb{X}_0\), the PEIBOS tool relies on a gnomonic atlas. See Gnomonic atlas.
Use
The computation of the reach set is decomposed into two separate functions.
PEIBOS
The PEIBOS function takes at least six arguments :
The CAPD IMap representing \(\gamma\).
The final time for the integration of the ODE.
A timestep to get intermediate states.
The inverse chart for the gnomonic atlas (an analytic function).
The list of symmetries for the gnomonic atlas. Note that each symmetry is represented as a hyperoctahedral symmetry, see sec-actions-octasym.
A resolution \(\epsilon\). The initial box \(\left[-1,1\right]^m\) will initiallly be splitted in boxes with a diameter smaller than \(\epsilon\).
Eventually an offset vector can be specified if the initial set is not centered around the origin.
Eventually a flag can be set to True to get the verbose.
For each of the small box \(\left[\mathbf{x}(0)\right]\), this function computes a box containing \(\bar{\mathbf{x}}(t)\) and an interval matrix enclosing the Jacobian matrix \(D\mathbf{\left[x\right]}(t)\).
It returns a map where the keys are the time steps. For each time step, the value is a vector of tuple, where each tuple contains:
A PEIBOS_CAPD_Key representing the symmetry and the initial box used (plus an eventual offset), i.e. \(\mathbf{x}(0)= \sigma(\psi_0(\mathbf{\text{box}})) + \text{offset}\)
A box enclosing \(\bar{\mathbf{x}}(t)\)
An interval matrix enclosing the Jacobian matrix \(D\mathbf{\left[x\right]}(t)\)
Each tuple can then be used to build a Parallelepiped enclosing \(\mathbf{x}(t)\) using the parallelepiped inclusion. This is done in the reach_set function.
The full signature of the function is :
-
std::map<double, std::vector<T>> codac2::PEIBOS(const capd::IMap &i_map, double tf, double dt, const AnalyticFunction<VectorType> &psi_0, const std::vector<OctaSym> &Sigma, double epsilon, const Vector &offset, bool verbose = false)
PEIBOS algorithm using CAPD for guaranteed ODE propagation.
- Parameters:
i_map – The CAPD interval map representing the ODE.
tf – Final time for the propagation.
dt – Time step for the output map.
psi_0 – The transformation function \(\psi_0:\mathbb{R}^m\rightarrow\mathbb{R}^n\) to construct the atlas
Sigma – The set of symmetry operators \(\sigma\) to construct the atlas
epsilon – The maximum diameter of the boxes to split \([-1,1]^m\) before computing the parallelepiped inclusions (each box is called “box” below)
offset – The offset to add to \(\sigma(\psi_0([-1,1]^m))\) (used to translate the initial manifold)
verbose – If true, print the time taken to compute the parallelepiped inclusions with other statistics
- Returns:
A timed map of PEIBOS CAPD results. At each time \(t\), the value is a vector of tuples. Each tuple contains:
A PEIBOS_CAPD_Key representing \(\mathbf{x}(0)= \sigma(\psi_0(\mathbf{\text{box}})) + \text{offset}\)
The interval vector \(\mathbf{z}\) containing the image \(\bar{\mathbf{x}}(t))\)
The interval Jacobian matrix \(\mathbf{J_f}\) containing \(D\mathbf{\left[x\right]}(t)\)
reach_set
The reach_set function takes only one argument : the map returned by the PEIBOS function.
It returns a map where the keys are the time steps. For each time step, the value is a vector of Parallelepiped. Their union is an outer approximation of \(\mathbf{X}_t\).
This function is then simply :
std::map<double,std::vector<Parallelepiped>> reach_set_(const std::map<double, std::vector<std::tuple<PEIBOS_CAPD_Key,IntervalVector,IntervalMatrix>>>& peibos_output)
{
std::map<double,std::vector<Parallelepiped>> output;
for (const auto& [time,vec] : peibos_output)
{
for (const auto& [key, z, Jf] : vec)
{
auto p = parallelepiped_inclusion_(z, Jf, Jf.mid(), key.psi_0, key.sigma, key.box);
output[time].push_back(p);
}
}
return output;
}
The parallelepiped_inclusion is the one described in this article.
Parallelepiped parallelepiped_inclusion_(const IntervalVector& Y, const IntervalMatrix& Jf, const Matrix& Jf_tild, const AnalyticFunction<VectorType>& psi_0, const OctaSym& sigma, const IntervalVector& X)
{
// Computation of the Jacobian of g = f o sigma(psi_0)
IntervalMatrix Jg = Jf * (sigma.permutation_matrix().template cast<Interval>()) * psi_0.diff(X);
Vector z = Y.mid();
// A is an approximation of the Jacobian of g at the center of X
Matrix A = (Jf_tild * sigma.permutation_matrix() * (psi_0.diff(X.mid()).mid()));
// Maximum error computation
double rho = error_peibos(Y, z, Jg, A, X);
// Inflation of the parallelepiped
Matrix A_inf = inflate_flat_parallelepiped(A, X.rad(), rho);
return Parallelepiped(z, A_inf);
}
Examples
2D : Pendulum
Say that we want to integrate the state of the pendulum starting from the an initial box. It is defined by
The corresponding code is:
capd::IMap vectorField_pend("par:l,g;var:t,w;fun:w,-sin(t)*5 - 0.5*w;");
VectorVar X_2d(1);
AnalyticFunction psi0_pend ({X_2d},{0.1*X_2d[0],0.1});
OctaSym id_2d ({1,2});
OctaSym s ({-2,1});
auto peibos_output_pend = PEIBOS(vectorField_pend, 20., 0.2, psi0_pend, {id_2d,s,s*s,s.invert()}, 0.02, {-M_PI/2.,0.});
auto m_v_par_2d_pend = reach_set(peibos_output_pend);
The result is
3D : Lorenz system
Say that we want to integrate the state of the pendulum starting from the an initial sphere. It is defined by
The corresponding code is:
capd::IMap vectorField_lorenz("par:sigma,rho,beta;var:x1,x2,x3;fun:10*(x2-x1),28*x1-x2-x1*x3,-2.6*x3+x1*x2;");
vectorField_lorenz.setParameter("sigma", 10.);
vectorField_lorenz.setParameter("rho", 28.);
vectorField_lorenz.setParameter("beta", 8/3);
VectorVar X_3d(2);
AnalyticFunction psi0_lorenz ({X_3d},{1/sqrt(1+sqr(X_3d[0])+sqr(X_3d[1])),X_3d[0]/sqrt(1+sqr(X_3d[0])+sqr(X_3d[1])),X_3d[1]/sqrt(1+sqr(X_3d[0])+sqr(X_3d[1]))});
OctaSym id_3d ({1,2,3});
OctaSym s1 ({-2,1,3});
OctaSym s2 ({3,2,-1});
auto peibos_output_lorenz = PEIBOS(vectorField_lorenz, 0.1, 0.05, psi0_lorenz, {id_3d,s1,s1*s1,s1.invert(),s2,s2.invert()}, 0.1);
auto m_v_par_lorenz = reach_set(peibos_output_lorenz);
The result is