Search code examples
computational-geometrynumericcgal

CGAL coordinate value transformation not working anymore in 5.2.1


In a project of mine I used to transform coordinate values from Kernel::FT to CGAL::Gmpq and back. The code does not compile anymore with the same kernel choice as before, namely

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;

But with the alternative I tried without being sure of it being completely equivalent to the former one everything compiled without errors:

typedef CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> > Kernel;

My header definitions

// the following kernel gives no error
//typedef CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> > Kernel;

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef Kernel::Circle_2                                  Circle_2;
typedef Kernel::Line_2                                    Line_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel>         Traits_2;

typedef CGAL::General_polygon_set_2<Traits_2>             Polygon_set_2;
typedef Traits_2::General_polygon_2                       Polygon_2;
typedef Traits_2::General_polygon_with_holes_2            Polygon_with_holes_2;
typedef Traits_2::Curve_2                                 Curve_2;
typedef Traits_2::X_monotone_curve_2                      X_monotone_curve_2;
typedef Traits_2::Point_2                                 Point_2t;
typedef Traits_2::CoordNT                                 coordnt;
typedef CGAL::Arrangement_2<Traits_2>                     Arrangement_2;
typedef Arrangement_2::Face_handle                        Face_handle;

The code that fails

CGAL::Gmpfr convert(CGAL::Gmpq z)
{
    CGAL::Gmpz num = z.numerator();
    CGAL::Gmpz den = z.denominator();

    CGAL::Gmpfr num_f(num);
    CGAL::Gmpfr den_f(den);

    return num_f/den_f;
}



CGAL::Gmpfr convert(Traits_2::CoordNT z, int use_precision)
{
    Kernel::FT a0_val = z.a0();
    Kernel::FT a1_val = z.a1();
    Kernel::FT root_val = z.root();

    //the following three lines give errors
    CGAL::Gmpq a0_q = a0_val.exact();
    CGAL::Gmpq a1_q = a1_val.exact();
    CGAL::Gmpq root_q = root_val.exact();


    CGAL::Gmpfr a0_f = convert(a0_q);
    CGAL::Gmpfr a1_f = convert(a1_q);
    CGAL::Gmpfr root_f = convert(root_q);

    CGAL::Gmpfr res = a0_f + a1_f * root_f.sqrt(use_precision);

    return res;
}


Point_2 convert(Point_2t p)
{
    CGAL::Gmpfr xx = convert(p.x());
    CGAL::Gmpfr yy = convert(p.y());

    CGAL::Gmpq xx1 = xx;
    CGAL::Gmpq yy1 = yy;
    
    // the following two lines give errors
    Kernel::FT xx2 = xx1;
    Kernel::FT yy2 = yy1;

    Point_2 pp(xx2, yy2);

    return pp;
}

The errors (the first three and the last two are essential identical):

cgal.cpp:48:32: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
  CGAL::Gmpq a0_q = a0_val.exact();
                    ~~~~~~~~~~~~^~
cgal.cpp:49:32: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
  CGAL::Gmpq a1_q = a1_val.exact();
                    ~~~~~~~~~~~~^~
cgal.cpp:50:36: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
  CGAL::Gmpq root_q = root_val.exact();
                      ~~~~~~~~~~~~~~^~
cgal.cpp: In function ‘Point_2 convert(Point_2t)’:
cgal.cpp:71:19: error: conversion from ‘CGAL::Gmpq’ to non-scalar type ‘CGAL::Lazy_kernel_generic_base<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> >, CGAL::Cartesian_converter<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> > >, CGAL::Epeck>::FT {aka CGAL::Lazy_exact_nt<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >}’ requested
  Kernel::FT xx2 = xx1;
                   ^~~
cgal.cpp:72:19: error: conversion from ‘CGAL::Gmpq’ to non-scalar type ‘CGAL::Lazy_kernel_generic_base<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> >, CGAL::Cartesian_converter<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> > >, CGAL::Epeck>::FT {aka CGAL::Lazy_exact_nt<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >}’ requested
  Kernel::FT yy2 = yy1;

It seems that the default kernel CGAL::Exact_predicates_exact_constructions_kernel now has a coordinate number type related to "boost-something" and this is not compatible to CGAL::Gmpq anymore.

What is the best solution to the problem, given that I want to use the fastest kernel equivalent to a CGAL::Exact_predicates_exact_constructions_kernel?

Shall I switch to CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> > or even to CGAL::Extended_cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> >?

Or can I set a certain environment variable so that CGAL::Exact_predicates_exact_construction_kernel works with CGAL::Gmpq by default instead of "boost-something", provided this does not make things slower?

Or can I just rewrite the offending lines with some newly introduced conversion operators/functions in CGAL 5.2.1. I am at the moment not aware of?


Solution

  • If you look at CGAL/Exact_predicates_exact_constructions_kernel.h to see how Epeck is defined, you can define you own version of Epeck using Gmpq as

    class Kernel
      : public Type_equality_wrapper<
                 Lazy_kernel_base< Simple_cartesian<Gmpq>,
                                   Simple_cartesian<Interval_nt_advanced>,
                                   Cartesian_converter< Simple_cartesian<Gmpq>,
                                                        Simple_cartesian<Interval_nt_advanced> >,
                                   Kernel>,
                 Kernel >
    {};
    

    (you may need to add CGAL:: all over)

    The rational type chosen by default depends on what is available, it can come from boost.Multiprecision (you can disable that choice with CGAL_DO_NOT_USE_BOOST_MP). It can come from GMPXX if CGAL_USE_GMPXX is defined. It can also be Gmpq (CGAL_USE_GMP but not CGAL_USE_GMPXX), leda_rational, etc.

    I understand this variability may not be ideal for not-so-usual cases like yours where you care about the type... I have never used Gmpfr, but it should not be hard to add constructors from mpz_class and mpz_int similar to the one from Gmpz (do you want to file an enhancement request on github, or possibly write it yourself? It may be a 1-liner, not counting the #ifdef). And then it would likely be possible to write your convertor in a way that is at least portable to all the GMP-based rationals, maybe using Fraction_traits<FT>::Decompose, although that will do an unnecessary copy. Actually, it may even make sense to add constructors to Gmpfr from rational types directly using mpfr_set_q.