Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/api/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -2502,7 +2502,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO
# The default value is: NO.

HAVE_DOT = YES
HAVE_DOT = NO

# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
Expand Down
14 changes: 14 additions & 0 deletions doc/manual/manual/intervals/Interval_class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,20 @@ All standard arithmetic operations are supported in Codac, both element-wise and
z = x / y % [1, 3]


Interval comparisons
--------------------

The comparison operators ``<`` and ``>`` are also overloaded for intervals.
They return a :ref:`BoolInterval value <sec-intervals-boolinterval-class>` rather than a classical Boolean, in order to encode the set of feasible truth values when the exact operands are only known within interval bounds.

For instance, ``x < y`` returns:

- ``BoolInterval.EMPTY`` if one of the two intervals is empty;
- ``BoolInterval.TRUE`` if :math:`\forall a\in[x],\forall b\in[y],\ a<b`;
- ``BoolInterval.FALSE`` if :math:`\forall a\in[x],\forall b\in[y],\ a\geqslant b`;
- ``BoolInterval.UNKNOWN`` otherwise.


Unary and binary functions
--------------------------

Expand Down
1 change: 1 addition & 0 deletions python/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
separators/codac2_py_SepInter.cpp
separators/codac2_py_SepInverse.cpp
separators/codac2_py_SepNot.cpp
separators/codac2_py_SepPolarCart.cpp
separators/codac2_py_SepPolygon.cpp
separators/codac2_py_SepProj.cpp
separators/codac2_py_SepQInter.cpp
Expand Down
2 changes: 2 additions & 0 deletions python/src/core/codac2_py_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ void export_SepCtcPair(py::module& m, py::class_<SepBase,pySep>& pysep);
void export_SepInter(py::module& m, py::class_<SepBase,pySep>& sep);
void export_SepInverse(py::module& m, py::class_<SepBase,pySep>& sep);
void export_SepNot(py::module& m, py::class_<SepBase,pySep>& sep);
void export_SepPolarCart(py::module& m, py::class_<SepBase,pySep>& pysep);
void export_SepPolygon(py::module& m, py::class_<SepBase,pySep>& sep);
void export_SepProj(py::module& m, py::class_<SepBase,pySep>& sep);
void export_SepQInter(py::module& m, py::class_<SepBase,pySep>& sep);
Expand Down Expand Up @@ -331,6 +332,7 @@ PYBIND11_MODULE(_core, m)
export_SepInter(m,py_sep);
export_SepInverse(m,py_sep);
export_SepNot(m,py_sep);
export_SepPolarCart(m,py_sep);
export_SepPolygon(m,py_sep);
export_SepProj(m,py_sep);
export_SepQInter(m,py_sep);
Expand Down
8 changes: 8 additions & 0 deletions python/src/core/domains/interval/codac2_py_Interval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ py::class_<Interval> export_Interval(py::module& m)
BOOL_INTERVAL_OPERATORNEQ_CONST_INTERVAL_REF_CONST,
"x"_a)

.def(py::self < py::self,
BOOLINTERVAL_INTERVAL_OPERATOR__CONST_INTERVAL_REF_CONST,
"x"_a)

.def(py::self > py::self,
BOOLINTERVAL_INTERVAL_OPERATOR_CONST_INTERVAL_REF_CONST,
"x"_a)

.def("lb", &Interval::lb,
DOUBLE_INTERVAL_LB_CONST)

Expand Down
39 changes: 39 additions & 0 deletions python/src/core/separators/codac2_py_SepPolarCart.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Codac binding (core)
* ----------------------------------------------------------------------------
* \date 2026
* \author Simon Rohou
* \copyright Copyright 2026 Codac Team
* \license GNU Lesser General Public License (LGPL)
*/

#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <pybind11/stl.h>
#include <codac2_template_tools.h>
#include <codac2_SepPolarCart.h>
#include "codac2_py_Sep.h"
#include "codac2_py_SepPolarCart_docs.h" // Generated file from Doxygen XML (doxygen2docstring.py):

using namespace std;
using namespace codac2;
namespace py = pybind11;
using namespace pybind11::literals;

void export_SepPolarCart(py::module& m, py::class_<SepBase,pySep>& pysep)
{
py::class_<SepPolarCart> exported(m, "SepPolarCart", pysep, SEPPOLARCART_MAIN);
exported

.def(py::init([](const SepBase& s1) {
return std::make_unique<SepPolarCart>(s1.copy());
}),
SEPPOLARCART_SEPPOLARCART_CONST_S_REF,
"s1"_a)

.def("separate", &SepPolarCart::separate,
BOXPAIR_SEPPOLARCART_SEPARATE_CONST_INTERVALVECTOR_REF_CONST,
"x"_a)

;
}
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepInter.h
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepInverse.h
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepNot.h
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepPolarCart.cpp
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepPolarCart.h
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepPolygon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepPolygon.h
${CMAKE_CURRENT_SOURCE_DIR}/separators/codac2_SepProj.cpp
Expand Down
35 changes: 35 additions & 0 deletions src/core/domains/interval/codac2_Interval.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "codac2_Domain.h"
#include "codac2_assert.h"
#include "codac2_TypeInfo.h"
#include "codac2_BoolInterval.h"

namespace codac2
{
Expand Down Expand Up @@ -160,6 +161,40 @@ namespace codac2
*/
bool operator!=(const Interval& x) const;

/**
* \brief Comparison (strict less-than) between this and x
*
* The returned ``BoolInterval`` encloses the truth value of
* \f$t<s\f$ for \f$t\in[\mathrm{this}]\f$ and \f$s\in[x]\f$.
*
* \note Returns:
* - ``BoolInterval::EMPTY`` if this or x is empty
* - ``BoolInterval::TRUE`` iff \f$\mathrm{ub}([\mathrm{this}])<\mathrm{lb}([x])\f$
* - ``BoolInterval::FALSE`` iff \f$\mathrm{ub}([x])\leq\mathrm{lb}([\mathrm{this}])\f$
* - ``BoolInterval::UNKNOWN`` otherwise
*
* \param x interval to be compared with
* \return interval Boolean result
*/
BoolInterval operator<(const Interval& x) const;

/**
* \brief Comparison (strict greater-than) between this and x
*
* The returned BoolInterval encloses the truth value of
* \f$t>s\f$ for \f$t\in[\mathrm{this}]\f$ and \f$s\in[x]\f$.
*
* \note Returns:
* - ``BoolInterval::EMPTY`` if this or x is empty
* - ``BoolInterval::TRUE`` iff \f$\mathrm{lb}([\mathrm{this}])>\mathrm{ub}([x])\f$
* - ``BoolInterval::FALSE`` iff \f$\mathrm{lb}([x])\geq\mathrm{ub}([\mathrm{this}])\f$
* - ``BoolInterval::UNKNOWN`` otherwise
*
* \param x interval to be compared with
* \return interval Boolean result
*/
BoolInterval operator>(const Interval& x) const;

/**
* \brief Returns the lower bound of this
*
Expand Down
16 changes: 16 additions & 0 deletions src/core/domains/interval/codac2_Interval_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ namespace codac2
return !(*this == x);
}

inline BoolInterval Interval::operator<(const Interval& x) const
{
if(is_empty() || x.is_empty())
return BoolInterval::EMPTY;
if(this->ub() < x.lb())
return BoolInterval::TRUE;
if(x.ub() <= this->lb())
return BoolInterval::FALSE;
return BoolInterval::UNKNOWN;
}

inline BoolInterval Interval::operator>(const Interval& x) const
{
return x < *this;
}

inline double Interval::lb() const
{
return gaol::interval::left();
Expand Down
141 changes: 91 additions & 50 deletions src/core/separators/codac2_SepPolarCart.cpp
Original file line number Diff line number Diff line change
@@ -1,74 +1,115 @@
/**
* codac2_SepPolarCart.cpp
* \file codac2_SepPolarCart.cpp
* ----------------------------------------------------------------------------
* \date 2026
* \author Benoît Desrochers, (Simon Rohou)
* \author Simon Rohou, from a former implementation of Benoît Desrochers
* \copyright Copyright 2026 Codac Team
* \license GNU Lesser General Public License (LGPL)
*/

#include "codac2_SepPolarCart.h"
#include "codac2_CtcPolar.h"
#include "codac2_cart_prod.h"
#include "codac2_hull.h"

using namespace codac2;

BoxPair SepPolarCart::separate(const IntervalVector& x) const
/**
* Contracts x with the Cartesian projection of the complement of a polar box.
*
* For a polar box [rho]x[theta], the complement is represented as the union
* of four polar boxes:
* - rho <= rho.lb(), with theta in [theta]
* - rho >= rho.ub(), with theta in [theta]
* - theta before theta.lb(), with rho in R+
* - theta after theta.ub(), with rho in R+
*
* The angular complement is computed in the 2*pi-wide window centered on [theta].
*/
IntervalVector contract_polar_box_complement(
const CtcPolar& ctc_polar,
const IntervalVector& x,
const IntervalVector& x_pol)
{
assert_release(x.size() == 2);
assert_release(x_pol.size() == 2);

CtcPolar ctc_polar;
IntervalVector x_cart(2), x_pol(x);
if(x.is_empty())
return x;

ctc_polar.contract(x_cart[0], x_cart[1], x_pol[0], x_pol[1]);
auto x_cart_sep = _sep.front()->separate(x_cart);
BoxPair x_polar_sep { x, x };
ctc_polar.contract(x_cart_sep.inner[0], x_cart_sep.inner[1], x_polar_sep.inner[0], x_polar_sep.inner[1]);
ctc_polar.contract(x_cart_sep.outer[0], x_cart_sep.outer[1], x_polar_sep.outer[0], x_polar_sep.outer[1]);
// Complement of the empty polar box: no point is removed by the inner
// contractor, so the Cartesian box is left unchanged.
if(x_pol.is_empty())
return x;

return x_polar_sep;
}
const Interval& rho = x_pol[0];
const Interval& theta = x_pol[1];

std::list<IntervalVector> parts;

SepPolarXY::SepPolarXY(Interval rho, Interval theta) : rho(rho), theta(theta), Sep(2) {
rho_m = Interval(0, rho.lb());
rho_p = Interval(rho.ub(), POS_INFINITY);
double limit = theta.mid() - M_PI;
theta_m = Interval(limit, theta.lb());
theta_p = Interval(theta.ub(), limit + 2*M_PI);
cmpl = Interval(0, 2*M_PI);
}
auto add_polar_part = [&ctc_polar,&parts](const IntervalVector& x,
const Interval& rho,
const Interval& theta)
{
if(rho.is_empty() || theta.is_empty())
return;

Interval rho_(rho), theta_(theta);
IntervalVector x_(x);
ctc_polar.contract(x_[0],x_[1],rho_,theta_);
parts.push_back(x_);
};

void SepPolarXY::contractOut(IntervalVector &x_out){
Interval th = this->theta;
Interval r = this->rho;
this->ctc.contract(x_out[0], x_out[1], r, th);
if(x_out[0].is_empty() || x_out[1].is_empty())
x_out.set_empty();
}
// Radial complement, restricted to the current angular sector. The angular
// outside parts below cover the remaining angles.
if(std::isfinite(rho.lb()) && rho.lb() >= 0)
add_polar_part(x, {0,rho.lb()}, theta);

if(std::isfinite(rho.ub()))
add_polar_part(x, {rho.ub(),oo}, theta);

// Angular complement. If [theta] already covers at least one full turn,
// there is no angular complement in the 2*pi-periodic sense.
if(std::isfinite(theta.lb()) && std::isfinite(theta.ub())
&& theta.ub() - theta.lb() < 2*PI)
{
double limit = theta.mid()-PI;
add_polar_part(x, {0,oo}, {limit,theta.lb()});
add_polar_part(x, {0,oo}, {theta.ub(),limit+2*PI});
}

void SepPolarXY::contractIn(IntervalVector &x_in){
Interval x1(x_in[0]); Interval y1(x_in[1]);
Interval x2(x_in[0]); Interval y2(x_in[1]);
Interval x3(x_in[0]); Interval y3(x_in[1]);
Interval x4(x_in[0]); Interval y4(x_in[1]);

Interval ALLREALS1 = Interval::POS_REALS;
Interval ALLREALS2 = Interval::POS_REALS;
Interval cmpl1(cmpl);
Interval cmpl2(cmpl);
Interval theta_m_tmp(theta_m);
Interval theta_p_tmp(theta_p);
Interval rho_m_tmp(rho_m);
Interval rho_p_tmp(rho_p);

this->ctc.contract(x1, y1, ALLREALS1, theta_m_tmp);
this->ctc.contract(x2, y2, ALLREALS2, theta_p_tmp);
this->ctc.contract(x3, y3, rho_m_tmp, cmpl1);
this->ctc.contract(x4, y4, rho_p_tmp, cmpl2);
x_in[0] &= (x1 | x2 | x3 | x4);
x_in[1] &= (y1 | y2 | y3 | y4);
if(x_in[0].is_empty() || x_in[1].is_empty())
x_in.set_empty();
return x & hull(parts);
}

BoxPair SepPolarCart::separate(const IntervalVector& x) const
{
assert_release(x.size() == 2);

BoxPair x_cart_sep { x, x };

if(x.is_empty())
return x_cart_sep;

CtcPolar ctc_polar;
IntervalVector x_cart(x), x_pol(2);

// Cartesian input -> polar enclosure
ctc_polar.contract(x_cart[0], x_cart[1], x_pol[0], x_pol[1]);

if(x_cart.is_empty() || x_pol.is_empty())
return x_cart_sep;

// Separation in the polar space
const BoxPair x_pol_sep = _sep.front()->separate(x_pol);

// Outer contraction: keep the Cartesian points whose polar coordinates may
// belong to the polar separator output
IntervalVector copy_x_pol_sep = x_pol_sep.outer.subvector(0,1);
ctc_polar.contract(x_cart_sep.outer[0], x_cart_sep.outer[1], copy_x_pol_sep[0], copy_x_pol_sep[1]);

// Inner contraction: remove points belonging to the polar set by contracting
// with the complement of the polar outer box
x_cart_sep.inner = contract_polar_box_complement(ctc_polar, x_cart_sep.inner, x_pol_sep.outer);

return x_cart_sep;
}
14 changes: 11 additions & 3 deletions src/core/separators/codac2_SepPolarCart.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* \file codac2_SepPolarCart.h
* ----------------------------------------------------------------------------
* \date 2026
* \author Benoît Desrochers, (Simon Rohou)
* \author Benoît Desrochers, Simon Rohou
* \copyright Copyright 2026 Codac Team
* \license GNU Lesser General Public License (LGPL)
*/
Expand All @@ -14,7 +14,15 @@

namespace codac2
{
// For separating a Cartesian box from a Sep expressed in polar coordinates
/**
* \class SepPolarCart
* \brief For separating a Cartesian box from a separator expressed in polar
* coordinates \f$(\rho,\theta)\f$.
*
* This separator is the inverse counterpart of SepCartPolar: the wrapped
* separator acts on polar boxes, while this class exposes a separator on
* Cartesian boxes \f$(x,y)\f$.
*/
class SepPolarCart : public Sep<SepPolarCart>
{
public:
Expand All @@ -33,4 +41,4 @@ namespace codac2

const Collection<SepBase> _sep;
};
}
}
Loading
Loading