使用 Boost ICL 交叉不同的间隔类型

Intersecting dfferent interval types with Boost ICL

我想检查 boost::icl::right_open_interval<double> i1boost::icl::closed_interval<double> i2 是否有交集。不幸的是,boost::icl::intersects(i1, i2) 函数仅在 i1i2 共享同一类型时才有效。我在想

  1. 为什么不支持不同的间隔类型,因为间隔库似乎很自然,并且
  2. 如果有一种很好的方法可以将上述函数推广到不同的区间类型。

有类型模型静态间隔概念:

还有动态间隔概念,它基本上添加了指示 interval_bounds:

的运行时状态
using icl::interval_bounds;
using std::is_same_v;

using I  = icl::interval<double>;
using CI = I::interval_type;

auto a = I::right_open(3, M_PI);
auto b = I::closed(3.14, 5);
auto c = I::left_open(7, std::numeric_limits<double>::infinity());

for (auto lhs : {a, b, c})
    for (auto rhs : {a, b, c})
        std::cout << lhs << " & " << rhs << " = " << (lhs & rhs) << "" << std::endl;

版画

[3,3.14159) & [3,3.14159) = [3,3.14159)
[3,3.14159) & [3.14,5] = [3.14,3.14159)
[3,3.14159) & (7,inf] = ()
[3.14,5] & [3,3.14159) = [3.14,3.14159)
[3.14,5] & [3.14,5] = [3.14,5]
[3.14,5] & (7,inf] = (]
(7,inf] & [3,3.14159) = ()
(7,inf] & [3.14,5] = (]
(7,inf] & (7,inf] = (7,inf]

有效类型都一样:

static_assert(is_same_v<decltype(a), CI>);
static_assert(is_same_v<decltype(b), CI>);
static_assert(is_same_v<decltype(c), CI>);
static_assert(is_same_v<CI, icl::continuous_interval<double>>);

您可以将此用于您的用例:

auto i1 = icl::right_open_interval<double>(3, M_PI);
auto i2 = icl::closed_interval<double>(3.14, 5);
auto i3 = icl::left_open_interval(7.0, std::numeric_limits<double>::infinity());

如您所知,它们的类型不同:

static_assert(not is_same_v<decltype(i1), decltype(i2)>);
static_assert(not is_same_v<decltype(i1), decltype(i3)>);
static_assert(not is_same_v<decltype(i2), decltype(i3)>);

存在这些静态间隔类型,因此库可以具有绝对最小的运行时间 overhead/storage。想象一下在数组中存储一百万个间隔。如果你知道类型,你真的想要承担大小开销:

static_assert(sizeof(i1) == sizeof(i2));
static_assert(sizeof(i2) == sizeof(i3));
static_assert(sizeof(I::interval_type) == sizeof(i1) + 8);

当然不是。

但是,正如您发现的那样,某些操作仅针对同类操作数静态实现。通用代码可以找出任何给定区间类型的边界类型:

static_assert(icl::interval_bound_type<decltype(i1)>::value == interval_bounds::static_right_open);
static_assert(icl::interval_bound_type<decltype(i2)>::value == interval_bounds::static_closed);
static_assert(icl::interval_bound_type<decltype(i3)>::value == interval_bounds::static_left_open);
static_assert(icl::interval_bound_type<CI>::value == interval_bounds::dynamic);

因此,您可以根据需要进行转换:

template <typename I>
auto make_dynamic(I const& in) {
    static_assert(icl::is_interval<I>::value);
    static_assert(icl::has_static_bounds<I>::value);

    using T = icl::interval_traits<I>;
    using D = typename T::domain_type;
    using R = std::conditional_t<icl::is_continuous<D>::value,
                                 icl::continuous_interval<D>,
                                 icl::discrete_interval<D>>;

    constexpr auto b = icl::interval_bound_type<I>::value;
    return icl::dynamic_interval_traits<R>::construct(
        T::lower(in), T::upper(in), static_cast<icl::interval_bounds>(b));
}

然后:

for (auto lhs : {make_dynamic(i1), make_dynamic(i2), make_dynamic(i3)})
    for (auto rhs : {make_dynamic(i1), make_dynamic(i2), make_dynamic(i3)})
    {
        std::cout << lhs << " & " << rhs << " = " << (lhs & rhs) << "" << std::endl;
        converted_results.push_back(lhs & rhs);
    }

打印与上面完全相同的内容:

[3,3.14159) & [3,3.14159) = [3,3.14159)
[3,3.14159) & [3.14,5] = [3.14,3.14159)
[3,3.14159) & (7,inf] = ()
[3.14,5] & [3,3.14159) = [3.14,3.14159)
[3.14,5] & [3.14,5] = [3.14,5]
[3.14,5] & (7,inf] = (]
(7,inf] & [3,3.14159) = ()
(7,inf] & [3.14,5] = (]
(7,inf] & (7,inf] = (7,inf]

下面是一个应用示例,展示了如何创建间隔映射:

using Set = std::set<char>;
boost::icl::interval_map<double, Set> s;
s.add({a, Set{'a'}});
s.add({b, Set{'b'}});
s.add({c, Set{'c'}});

std::cout << "s: " << s << "\n";

打印:

s: {([3,3.14)->{a })([3.14,3.14159)->{a b })([3.14159,5]->{b })((7,inf]->{c })}

查看全部代码Live On Coliru

推荐

这一切看起来有点笨拙,对吧。因此我建议选择一个阵营:动态边界或静态边界。

如果你真的想要静态边界,考虑定义 BOOST_ICL_USE_STATIC_BOUNDED_INTERVALS:

// If this macro is defined, right_open_interval with static interval borders
// will be used as default for all interval containers. 
// BOOST_ICL_USE_STATIC_BOUNDED_INTERVALS should be defined in the application
// before other includes from the ITL
//#define BOOST_ICL_USE_STATIC_BOUNDED_INTERVALS
// If BOOST_ICL_USE_STATIC_BOUNDED_INTERVALS is NOT defined, ITL uses intervals
// with dynamic borders as default.

请注意,这具有例如始终使用 right-open 间隔制作间隔 sets/maps。请参阅更详细的示例:https://www.boost.org/doc/libs/1_78_0/libs/icl/doc/html/boost_icl/examples/static_interval.html, and the Interface documentation.

列表

供将来参考:

#include <boost/icl/interval_map.hpp>
#include <iostream>
namespace icl = boost::icl;

template <typename I>
auto make_dynamic(I const& in) {
    static_assert(icl::is_interval<I>::value);
    static_assert(icl::has_static_bounds<I>::value);

    using T = icl::interval_traits<I>;
    using D = typename T::domain_type;
    using R = std::conditional_t<icl::is_continuous<I>::value,
                                 icl::continuous_interval<D>,
                                 icl::discrete_interval<D>>;

    constexpr auto b = icl::interval_bound_type<I>::value;
    return icl::dynamic_interval_traits<R>::construct(
        T::lower(in), T::upper(in), static_cast<icl::interval_bounds>(b));
}

int main() {
    using icl::interval_bounds;
    using std::is_same_v;

    using I  = icl::interval<double>;
    using CI = I::interval_type;

    auto a = I::right_open(3, M_PI);
    auto b = I::closed(3.14, 5);
    auto c = I::left_open(7, std::numeric_limits<double>::infinity());

    std::cout << "--------------" << std::endl;
    std::vector<CI> dynamic_results;
    for (auto lhs : {a, b, c})
        for (auto rhs : {a, b, c})
        {
            std::cout << lhs << " & " << rhs << " = " << (lhs & rhs) << "" << std::endl;
            dynamic_results.push_back(lhs & rhs);
        }

    static_assert(is_same_v<decltype(a), CI>);
    static_assert(is_same_v<decltype(b), CI>);
    static_assert(is_same_v<decltype(c), CI>);
    static_assert(is_same_v<CI, icl::continuous_interval<double>>);


    auto i1 = icl::right_open_interval<double>(3, M_PI);
    auto i2 = icl::closed_interval<double>(3.14, 5);
    auto i3 = icl::left_open_interval(7.0, std::numeric_limits<double>::infinity());
    static_assert(not is_same_v<decltype(i1), decltype(i2)>);
    static_assert(not is_same_v<decltype(i1), decltype(i3)>);
    static_assert(not is_same_v<decltype(i2), decltype(i3)>);
    static_assert(sizeof(i1) == sizeof(i2));
    static_assert(sizeof(i2) == sizeof(i3));
    static_assert(sizeof(I::interval_type) == sizeof(i1) + 8);

    static_assert(icl::interval_bound_type<decltype(i1)>::value == interval_bounds::static_right_open);
    static_assert(icl::interval_bound_type<decltype(i2)>::value == interval_bounds::static_closed);
    static_assert(icl::interval_bound_type<decltype(i3)>::value == interval_bounds::static_left_open);
    static_assert(icl::interval_bound_type<CI>::value == interval_bounds::dynamic);
    
    std::cout << "--------------" << std::endl;
    std::vector<CI> converted_results;
    for (auto lhs : {make_dynamic(i1), make_dynamic(i2), make_dynamic(i3)})
        for (auto rhs : {make_dynamic(i1), make_dynamic(i2), make_dynamic(i3)})
        {
            std::cout << lhs << " & " << rhs << " = " << (lhs & rhs) << "" << std::endl;
            converted_results.push_back(lhs & rhs);
        }

    assert(dynamic_results == converted_results);

    using Set = std::set<char>;
    boost::icl::interval_map<double, Set> s;
    s.add({a, Set{'a'}});
    s.add({b, Set{'b'}});
    s.add({c, Set{'c'}});

    std::cout << "s: " << s << "\n";
}

版画

--------------
[3,3.14159) & [3,3.14159) = [3,3.14159)
[3,3.14159) & [3.14,5] = [3.14,3.14159)
[3,3.14159) & (7,inf] = ()
[3.14,5] & [3,3.14159) = [3.14,3.14159)
[3.14,5] & [3.14,5] = [3.14,5]
[3.14,5] & (7,inf] = (]
(7,inf] & [3,3.14159) = ()
(7,inf] & [3.14,5] = (]
(7,inf] & (7,inf] = (7,inf]
--------------
[3,3.14159) & [3,3.14159) = [3,3.14159)
[3,3.14159) & [3.14,5] = [3.14,3.14159)
[3,3.14159) & (7,inf] = ()
[3.14,5] & [3,3.14159) = [3.14,3.14159)
[3.14,5] & [3.14,5] = [3.14,5]
[3.14,5] & (7,inf] = (]
(7,inf] & [3,3.14159) = ()
(7,inf] & [3.14,5] = (]
(7,inf] & (7,inf] = (7,inf]
s: {([3,3.14)->{a })([3.14,3.14159)->{a b })([3.14159,5]->{b })((7,inf]->{c })}