From 3a01429a4356f5d4805ffb53ef3e5ac8ea3aa721 Mon Sep 17 00:00:00 2001 From: Allan Leal Date: Fri, 2 Jun 2023 21:34:15 +0200 Subject: [PATCH 1/4] Improved implicit conversion operators for real, dual and var --- autodiff/forward/real/real.hpp | 13 ++++++++----- autodiff/reverse/var/var.hpp | 15 ++++++++++++--- tests/CMakeLists.txt | 8 ++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/autodiff/forward/real/real.hpp b/autodiff/forward/real/real.hpp index d564edad..3b5b500e 100644 --- a/autodiff/forward/real/real.hpp +++ b/autodiff/forward/real/real.hpp @@ -186,11 +186,14 @@ class Real return *this; } - /// Convert this Real number into a value of type @p U. #if defined(AUTODIFF_ENABLE_IMPLICIT_CONVERSION_REAL) || defined(AUTODIFF_ENABLE_IMPLICIT_CONVERSION) + constexpr operator T() const { return static_cast(m_data[0]); } + template> = true> constexpr operator U() const { return static_cast(m_data[0]); } #else + constexpr explicit operator T() const { return static_cast(m_data[0]); } + template> = true> constexpr explicit operator U() const { return static_cast(m_data[0]); } #endif @@ -864,13 +867,13 @@ constexpr auto min(const Real& x, const Real& y) template> = true> constexpr auto min(const Real& x, const U& y) { - return (x[0] <= y) ? x : y; + return (x[0] <= y) ? x : Real{y}; } template> = true> constexpr auto min(const U& x, const Real& y) { - return (x < y[0]) ? x : y; + return (x < y[0]) ? Real{x} : y; } template @@ -882,13 +885,13 @@ constexpr auto max(const Real& x, const Real& y) template> = true> constexpr auto max(const Real& x, const U& y) { - return (x[0] >= y) ? x : y; + return (x[0] >= y) ? x : Real{y}; } template> = true> constexpr auto max(const U& x, const Real& y) { - return (x > y[0]) ? x : y; + return (x > y[0]) ? Real{x} : y; } //===================================================================================================================== diff --git a/autodiff/reverse/var/var.hpp b/autodiff/reverse/var/var.hpp index 779e8564..ba47b4a1 100644 --- a/autodiff/reverse/var/var.hpp +++ b/autodiff/reverse/var/var.hpp @@ -1219,9 +1219,6 @@ struct Variable /// Implicitly convert this Variable object into an expression pointer. operator const ExprPtr&() const { return expr; } - /// Explicitly convert this Variable object into its underlying arithmetic type. - explicit operator T() const { return expr->val; } - /// Assign an arithmetic value to this variable. template> = true> auto operator=(const U& val) -> Variable& { *this = Variable(val); return *this; } @@ -1240,6 +1237,18 @@ struct Variable template> = true> Variable& operator-=(const U& x) { *this = Variable(expr - x); return *this; } template> = true> Variable& operator*=(const U& x) { *this = Variable(expr * x); return *this; } template> = true> Variable& operator/=(const U& x) { *this = Variable(expr / x); return *this; } + +#if defined(AUTODIFF_ENABLE_IMPLICIT_CONVERSION_VAR) || defined(AUTODIFF_ENABLE_IMPLICIT_CONVERSION) + operator T() const { return expr->val; } + + template + operator U() const { return static_cast(expr->val); } +#else + explicit operator T() const { return expr->val; } + + template + explicit operator U() const { return static_cast(expr->val); } +#endif }; //------------------------------------------------------------------------------ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a4dfc530..1eefb25a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,10 +6,16 @@ add_executable(autodiff-cpptests main.cpp ${TEST_FILES}) target_link_libraries(autodiff-cpptests autodiff::autodiff Eigen3::Eigen Catch2::Catch2WithMain) set_target_properties(autodiff-cpptests PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) +add_executable(autodiff-cpptests-implicit-conversion main.cpp ${TEST_FILES}) +target_compile_definitions(autodiff-cpptests-implicit-conversion PUBLIC AUTODIFF_ENABLE_IMPLICIT_CONVERSION=1) +target_link_libraries(autodiff-cpptests-implicit-conversion autodiff::autodiff Eigen3::Eigen Catch2::Catch2WithMain) +set_target_properties(autodiff-cpptests-implicit-conversion PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF) + # Add target tests that performs all C++ and Python tests add_custom_target(tests COMMENT "Running C++ tests..." COMMAND $ + COMMAND $ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) if(AUTODIFF_TEST_SANITIZE) @@ -19,6 +25,8 @@ if(AUTODIFF_TEST_SANITIZE) unset(CMAKE_REQUIRED_FLAGS) if(HAVE_ASAN) target_compile_options(autodiff-cpptests PRIVATE "-fsanitize=address") + target_compile_options(autodiff-cpptests-implicit-conversion PRIVATE "-fsanitize=address") target_link_options(autodiff-cpptests PRIVATE "-fsanitize=address") + target_link_options(autodiff-cpptests-implicit-conversion PRIVATE "-fsanitize=address") endif() endif() From 5832576d587354dc20de37391f51f70c70bf1847 Mon Sep 17 00:00:00 2001 From: Allan Leal Date: Fri, 2 Jun 2023 21:34:28 +0200 Subject: [PATCH 2/4] Increment version to 1.0.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02491f71..8e9463eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(CCache) # Name and details of the project -project(autodiff VERSION 1.0.2 LANGUAGES CXX) +project(autodiff VERSION 1.0.3 LANGUAGES CXX) # Enable parallel build if MSVC is used add_compile_options($<$:/MP>) From b511ee2a14abbd95feb75d69f57ade1d2eddb470 Mon Sep 17 00:00:00 2001 From: Allan Leal Date: Fri, 2 Jun 2023 21:47:33 +0200 Subject: [PATCH 3/4] Update README.md --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index cde73bd1..95b9f8ff 100644 --- a/README.md +++ b/README.md @@ -143,16 +143,6 @@ be extracted from `dud` using `dud(x)`. The operations `dud(x)`, `dud(y)`, `dud(z)` involve no computations! Just extraction of derivatives previously computed with a call to function `autodiff::derivatives`. -## Development status - -**autodiff** is planned to be a long-term maintained automatic differentiation -C++ project. This means that more algorithms and number types will still be -implemented and that the currently existing ones will be further enhanced. -Please have in mind, however, that **autodiff** is still in a relatively early -stage of development, which implies that minor breaking changes in API may be -introduced to simplify its use and make it more intuitive and consistent with -new library additions. - ## Documentation Check the documentation website for more details: @@ -165,7 +155,7 @@ Check the documentation website for more details: MIT License -Copyright (c) 2018–2021 Allan Leal +Copyright (c) 2018–2023 Allan Leal Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From f64c907397446434ffe2809cbf84925da7aeef91 Mon Sep 17 00:00:00 2001 From: Allan Leal Date: Mon, 5 Jun 2023 10:25:43 +0200 Subject: [PATCH 4/4] Fixed tests failing in Windows due to Catch2::Approx not able to resolve implit conversion --- tests/forward/dual/dual.test.cpp | 30 ++++++++++++++++++++---------- tests/forward/dual/eigen.test.cpp | 14 ++++++++------ tests/reverse/var/var.test.cpp | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/tests/forward/dual/dual.test.cpp b/tests/forward/dual/dual.test.cpp index 519c6108..6826fc40 100644 --- a/tests/forward/dual/dual.test.cpp +++ b/tests/forward/dual/dual.test.cpp @@ -159,33 +159,43 @@ TEST_CASE("testing autodiff::dual", "[forward][dual]") SECTION("trivial tests") { - CHECK( x == approx(100) ); + CHECK( val(x) == approx(100) ); + CHECK( grad(x) == 0.0 ); x += 1; - CHECK( x == approx(101) ); + CHECK( val(x) == approx(101) ); + CHECK( grad(x) == 0.0 ); x -= 1; - CHECK( x == approx(100) ); + CHECK( val(x) == approx(100) ); + CHECK( grad(x) == 0.0 ); x *= 2; - CHECK( x == approx(200) ); + CHECK( val(x) == approx(200) ); + CHECK( grad(x) == 0.0 ); x /= 20; - CHECK( x == approx(10) ); + CHECK( val(x) == approx(10) ); + CHECK( grad(x) == 0.0 ); } SECTION("aliasing tests") { x = 1; x = x + 3*x - 2*x + x; - CHECK( x == approx(3) ); + CHECK( val(x) == approx(3) ); + CHECK( grad(x) == 0.0 ); x = 1; x += x + 3*x - 2*x + x; - CHECK( x == approx(4) ); + CHECK( val(x) == approx(4) ); + CHECK( grad(x) == 0.0 ); x = 1; x -= x + 3*x - 2*x + x; - CHECK( x == approx(-2) ); + CHECK( val(x) == approx(-2) ); + CHECK( grad(x) == 0.0 ); x = 1; x *= x + 3*x - 2*x + x; - CHECK( x == approx(3) ); + CHECK( val(x) == approx(3) ); + CHECK( grad(x) == 0.0 ); x = 1; x /= x + x; - CHECK( x == approx(0.5) ); + CHECK( val(x) == approx(0.5) ); + CHECK( grad(x) == 0.0 ); } SECTION("testing comparison operators") diff --git a/tests/forward/dual/eigen.test.cpp b/tests/forward/dual/eigen.test.cpp index c8f254df..5c1f93cf 100644 --- a/tests/forward/dual/eigen.test.cpp +++ b/tests/forward/dual/eigen.test.cpp @@ -43,6 +43,8 @@ auto approx(T&& expr) -> Catch::Approx return Catch::Approx(val(std::forward(expr))).margin(zero); } +#define CHECK_APPROX(a, b) CHECK( abs(a - b) < abs(b) * std::numeric_limits::epsilon() * 100 ); + TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") { using Eigen::MatrixXd; @@ -106,7 +108,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") VectorXd y = x.cast(); for(auto i = 0; i < 3; ++i) - CHECK( x[i] == approx(y(i)) ); + CHECK_APPROX( x[i], y(i) ); } SECTION("testing casting to MatriXd") @@ -118,7 +120,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") for(auto i = 0; i < 2; ++i) for(auto j = 0; j < 2; ++j) - CHECK(x(i, j) == approx(y(i, j))); + CHECK_APPROX( x(i, j), y(i, j) ); } SECTION("testing multiplication of VectorXdual by MatrixXd") @@ -248,7 +250,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") VectorXd y = x.cast(); for(auto i = 0; i < 3; ++i) - CHECK( x(i) == approx(y(i)) ); + CHECK_APPROX( x(i), y(i) ); } SECTION("testing casting to VectorXf") @@ -258,7 +260,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") MatrixXd y = x.cast(); for(auto i = 0; i < 2; ++i) for(auto j = 0; j < 2; ++j) - CHECK( x(i, j) == approx(y(i, j)) ); + CHECK_APPROX( x(i, j), y(i, j) ); } SECTION("test gradient size with respect to few arguments") @@ -385,7 +387,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") VectorXd y = x.template cast(); for(auto i = 0; i < 3; ++i) - CHECK( x(i) == approx(y(i)) ); + CHECK_APPROX( x(i), y(i) ); } using dual2nd = HigherOrderDual<2, double>; @@ -457,7 +459,7 @@ TEST_CASE("testing autodiff::dual (with eigen)", "[forward][dual][eigen]") VectorXd y = x.template cast(); for(auto i = 0; i < 3; ++i) - CHECK( x(i) == approx(y(i)) ); + CHECK_APPROX( x(i), y(i) ); } SECTION("testing gradient derivatives") diff --git a/tests/reverse/var/var.test.cpp b/tests/reverse/var/var.test.cpp index d534ce84..7dad01e4 100644 --- a/tests/reverse/var/var.test.cpp +++ b/tests/reverse/var/var.test.cpp @@ -326,7 +326,7 @@ TEST_CASE("testing autodiff::var", "[reverse][var]") y = 2 * a; - REQUIRE( y == Catch::Approx( 2 * val(a) ) ); + REQUIRE( val(y) == Catch::Approx( 2 * val(a) ) ); REQUIRE( grad(y, a) == Catch::Approx( 2.0 ) ); REQUIRE( val(pow(x, y)) == Catch::Approx(std::pow(val(x), val(y))) );