Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building against libcxxwrap-julia require definition of comparison operators between incompatible types #173

Open
afossa opened this issue Sep 19, 2024 · 7 comments

Comments

@afossa
Copy link

afossa commented Sep 19, 2024

I am building a Julia interface to dace using libcxxwrap-julia and CxxWrap, and I recently came across a compilation error that requires me to define the "less than" operator between types for which is not defined, i.e. DACE::DA and DACE::AlgebraicMatrix<DACE::DA>.

Here is the output I obtain when trying to build the wrapper on Ubuntu 22.04 LTS:
build.log
config.log

I started obtaining this error after defining the comparison operators ==,!=,<,>,<=,>= between DACE::DA objects and double in interfaces/cxx/include/dace/DA.h

    friend inline bool DACE_API operator==(const DA &da1, const DA &da2) { return da1.cons() == da2.cons(); };  //!< Equality comparison
    friend inline bool DACE_API operator==(const DA &da, const double c) { return da.cons() == c; };            //!< Equality comparison
    friend inline bool DACE_API operator==(const double c, const DA &da) { return c == da.cons(); };            //!< Equality comparison

    friend inline bool DACE_API operator!=(const DA &da1, const DA &da2) { return da1.cons() != da2.cons(); };  //!< Inequality comparison
    friend inline bool DACE_API operator!=(const DA &da, const double c) { return da.cons() != c; };            //!< Inequality comparison
    friend inline bool DACE_API operator!=(const double c, const DA &da) { return c != da.cons(); };            //!< Inequality comparison

    friend inline bool DACE_API operator<(const DA &da1, const DA &da2) { return da1.cons() < da2.cons(); };    //!< Less than comparison
    friend inline bool DACE_API operator<(const DA &da, const double c) { return da.cons() < c; };              //!< Less than comparison
    friend inline bool DACE_API operator<(const double c, const DA &da) { return c < da.cons(); };              //!< Less than comparison

    friend inline bool DACE_API operator>(const DA &da1, const DA &da2) { return da1.cons() > da2.cons(); };    //!< Greater than comparison
    friend inline bool DACE_API operator>(const DA &da, const double c) { return da.cons() > c; };              //!< Greater than comparison
    friend inline bool DACE_API operator>(const double c, const DA &da) { return c > da.cons(); };              //!< Greater than comparison

    friend inline bool DACE_API operator<=(const DA &da1, const DA &da2) { return da1.cons() <= da2.cons(); };  //!< Less than or equal comparison
    friend inline bool DACE_API operator<=(const DA &da, const double c) { return da.cons() <= c; };            //!< Less than or equal comparison
    friend inline bool DACE_API operator<=(const double c, const DA &da) { return c <= da.cons(); };            //!< Less than or equal comparison

    friend inline bool DACE_API operator>=(const DA &da1, const DA &da2) { return da1.cons() >= da2.cons(); };  //!< Greater than or equal comparison
    friend inline bool DACE_API operator>=(const DA &da, const double c) { return da.cons() >= c; };            //!< Greater than or equal comparison
    friend inline bool DACE_API operator>=(const double c, const DA &da) { return c >= da.cons(); };            //!< Greater than or equal comparison

I am also able to overcome it by adding these dummy definitions at the top of the interface interfaces/julia/dace_julia.cxx

namespace DACE {
    inline bool DACE_API operator<(const AlgebraicMatrix<DA> &mat, const DA &da) { throw std::runtime_error("Comparison between DA and AlgebraicMatrix not defined"); };
    inline bool DACE_API operator<(const DA &da, const AlgebraicMatrix<DA> &mat) { throw std::runtime_error("Comparison between DA and AlgebraicMatrix not defined"); };
    inline bool DACE_API operator<(const AlgebraicMatrix<DA> &mt1, const AlgebraicMatrix<DA> &mt2) { throw std::runtime_error("Comparison between two AlgebraicMatrix not defined"); };
}

but this is really a workaround that shouldn't be there.

More surprisingly, I do not encounter the same error with DACE::AlgebraicVector<DA>, which is wrapped pretty much in the same way as DACE::AlgebraicMatrix<DA> is. Building the library without the Julia wrapper bits also works, so it is really a matter of how it interacts with libcxxwrap-julia.

Do you have any insight on the root cause of this error? Thanks!

@barche
Copy link
Contributor

barche commented Sep 19, 2024

Is this against the latest libcxxwrap-julia (main branch)? It is probably related to the added support for more STL containters, maybe this check isn't working:

if constexpr (container_has_less_than_operator<T>::value)

@afossa
Copy link
Author

afossa commented Sep 19, 2024

It happens both against main and against the latest release v0.13.2. But yes, it seems related to the instantiation of STL containers. The logs I attached are actually against the latest release and not main.

@barche
Copy link
Contributor

barche commented Sep 20, 2024

OK, if you don't need sets or maps then you can comment out these lines in apply_stl as a temporary workaround. I was already working on refactoring this part of the code, so only the part of the STL that is requested gets wrapped, i.e. if you use a std::vector with your type, only vector will be wrapped and not all supported containers.

@afossa
Copy link
Author

afossa commented Sep 20, 2024

Thank you for the hint!

I did a couple of other tests, and it seems that the check you mentioned before does work in most of the cases, but it gets "confused" when the type to which the STL is applied is itself a kind of "container". What I have observed is the following:

I have my wrapped type DA on which I explicitly apply the STL, i.e.:

jlcxx::stl::apply_stl<DA>(mod)

Then I have the parametric type AlgebraicMatrix<T> which is defined for T being a DA or a double as:

mod.add_type<jlcxx::Parametric<jlcxx::TypeVar<1>>>("AlgebraicMatrix", jlcxx::julia_type("AbstractMatrix", "Base"))
    .apply<AlgebraicMatrix<DA>, AlgebraicMatrix<double>>([](auto wrapped) {
...
});

The type DA is used several times as the element type of std::vectors, while AlgebraicMatrix<T> is used as element of an STL container (a vector) only once:

mod.method("hessian", [](const AlgebraicVector<DA>& vec)->std::vector<AlgebraicMatrix<DA>> { return vec.hessian(); });

Moreover, the DA type defines the "less than" and the other comparison operators between DAs, and between a DA and a double.

To get a compiler error, I need both the hessian() method being wrapped, AND the comparison operators being defined for DAs. If I remove either one of the two, the wrapper is compiled just fine, and I can call it from Julia.

@barche
Copy link
Contributor

barche commented Nov 7, 2024

Can you verify if PR #175 fixes this for you?

@afossa
Copy link
Author

afossa commented Nov 8, 2024

I recompiled the library from your branch test-compile-times, and I can confirm that it fixes my issue.
I can now define hessian() as in my previous comment, and have it return a std::vector<AlgebraicMatrix<DA>> without problems.

Tested on both Ubuntu 22.04 (gcc) and MacOS Sonoma (Apple Clang, Intel chip)

Thanks!

@barche
Copy link
Contributor

barche commented Nov 8, 2024

Thanks for testing, I'll integrate this into a new release. Unfortunately the changes are not binary compatible with previous versions, so this will become part of CxxWrap 0.17.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants