Skip to content

Commit dac3672

Browse files
committed
Switch to CPM; support 3rd-order tensor jacobians
1 parent 60f9fee commit dac3672

File tree

9 files changed

+154
-101
lines changed

9 files changed

+154
-101
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ endif()
3535
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/finitediff/")
3636
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/recipes/")
3737

38+
include(finitediff_cpm_cache)
39+
3840
################################################################################
3941
# Finite Diff Library
4042
################################################################################

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The library provides three main functions `finite_gradient`, `finite_jacobian`,
4343

4444
```c++
4545
void finite_gradient(
46-
const Eigen::VectorXd& x,
46+
const Eigen::Ref<const Eigen::VectorXd>& x,
4747
const std::function<double(const Eigen::VectorXd&)>& f,
4848
Eigen::VectorXd& grad,
4949
const AccuracyOrder accuracy = SECOND,
@@ -56,8 +56,8 @@ The `finite_gradient` function computes the [gradient](https://en.wikipedia.org/
5656
5757
```c++
5858
void finite_jacobian(
59-
const Eigen::VectorXd& x,
60-
const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& f,
59+
const Eigen::Ref<const Eigen::VectorXd>& x,
60+
const std::function<Eigen::MatrixXd(const Eigen::VectorXd&)>& f,
6161
Eigen::MatrixXd& jac,
6262
const AccuracyOrder accuracy = SECOND,
6363
const double eps = 1.0e-8);
@@ -70,7 +70,7 @@ The `finite_jacobian` function computes the [Jacobian](https://en.wikipedia.org/
7070

7171
```c++
7272
void finite_hessian(
73-
const Eigen::VectorXd& x,
73+
const Eigen::Ref<const Eigen::VectorXd>& x,
7474
const std::function<double(const Eigen::VectorXd&)>& f,
7575
Eigen::MatrixXd& hess,
7676
const AccuracyOrder accuracy = SECOND,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#
2+
# Copyright 2021 Adobe. All rights reserved.
3+
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License. You may obtain a copy
5+
# of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software distributed under
8+
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
# OF ANY KIND, either express or implied. See the License for the specific language
10+
# governing permissions and limitations under the License.
11+
#
12+
13+
if(DEFINED ENV{CPM_SOURCE_CACHE})
14+
set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE})
15+
else()
16+
# Set CPM cache folder if unset
17+
file(REAL_PATH "~/.cache/CPM" CPM_SOURCE_CACHE_DEFAULT EXPAND_TILDE)
18+
endif()
19+
20+
set(CPM_SOURCE_CACHE
21+
${CPM_SOURCE_CACHE_DEFAULT}
22+
CACHE PATH "Directory to download CPM dependencies"
23+
)
24+
message(STATUS "Using CPM cache folder: ${CPM_SOURCE_CACHE}")

cmake/recipes/CPM.cmake

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
set(CPM_DOWNLOAD_VERSION 0.40.2)
2+
3+
if(CPM_SOURCE_CACHE)
4+
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
5+
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
6+
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
7+
else()
8+
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
9+
endif()
10+
11+
# Expand relative path. This is important if the provided path contains a tilde (~)
12+
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
13+
14+
function(download_cpm)
15+
message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}")
16+
file(DOWNLOAD
17+
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
18+
${CPM_DOWNLOAD_LOCATION}
19+
)
20+
endfunction()
21+
22+
if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))
23+
download_cpm()
24+
else()
25+
# resume download if it previously failed
26+
file(READ ${CPM_DOWNLOAD_LOCATION} check)
27+
if("${check}" STREQUAL "")
28+
download_cpm()
29+
endif()
30+
unset(check)
31+
endif()
32+
33+
include(${CPM_DOWNLOAD_LOCATION})

cmake/recipes/catch2.cmake

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
1-
#
2-
# Copyright 2020 Adobe. All rights reserved.
3-
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
4-
# you may not use this file except in compliance with the License. You may obtain a copy
5-
# of the License at http://www.apache.org/licenses/LICENSE-2.0
6-
#
7-
# Unless required by applicable law or agreed to in writing, software distributed under
8-
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9-
# OF ANY KIND, either express or implied. See the License for the specific language
10-
# governing permissions and limitations under the License.
11-
#
1+
# Catch2 (https://github.com/catchorg/Catch2)
2+
# License: BSL-1.0
123
if(TARGET Catch2::Catch2)
134
return()
145
endif()
156

167
message(STATUS "Third-party: creating target 'Catch2::Catch2'")
178

18-
include(FetchContent)
19-
FetchContent_Declare(
20-
catch2
21-
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
22-
GIT_TAG v3.0.1
23-
GIT_SHALLOW TRUE
24-
)
25-
FetchContent_MakeAvailable(catch2)
9+
option(CATCH_CONFIG_CPP17_STRING_VIEW "Enable support for std::string_view" ON)
10+
option(CATCH_INSTALL_DOCS "Install documentation alongside library" OFF)
11+
option(CATCH_INSTALL_EXTRAS "Install extras alongside library" OFF)
12+
13+
include(CPM)
14+
CPMAddPackage("gh:catchorg/[email protected]")
15+
16+
# Folder name for IDE
17+
set_target_properties(Catch2 PROPERTIES FOLDER "ThirdParty/Catch2")
18+
set_target_properties(Catch2WithMain PROPERTIES FOLDER "ThirdParty/Catch2")

cmake/recipes/eigen.cmake

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,39 @@
1-
#
2-
# Copyright 2020 Adobe. All rights reserved.
3-
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
4-
# you may not use this file except in compliance with the License. You may obtain a copy
5-
# of the License at http://www.apache.org/licenses/LICENSE-2.0
6-
#
7-
# Unless required by applicable law or agreed to in writing, software distributed under
8-
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9-
# OF ANY KIND, either express or implied. See the License for the specific language
10-
# governing permissions and limitations under the License.
11-
#
1+
# Eigen (https://gitlab.com/libeigen/eigen)
2+
# License: MPL 2.0
123
if(TARGET Eigen3::Eigen)
134
return()
145
endif()
156

167
option(EIGEN_WITH_MKL "Use Eigen with MKL" OFF)
8+
option(EIGEN_DONT_VECTORIZE "Disable Eigen vectorization" OFF)
9+
option(EIGEN_MPL2_ONLY "Enable Eigen MPL2 license only" OFF)
1710

18-
if(EIGEN_ROOT)
19-
message(STATUS "Third-party: creating target 'Eigen3::Eigen' for external path: ${EIGEN_ROOT}")
20-
set(EIGEN_INCLUDE_DIRS ${EIGEN_ROOT})
21-
else()
22-
message(STATUS "Third-party: creating target 'Eigen3::Eigen'")
11+
message(STATUS "Third-party: creating target 'Eigen3::Eigen'")
2312

24-
include(FetchContent)
25-
FetchContent_Declare(
26-
eigen
27-
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
28-
GIT_TAG tags/3.4.0
29-
GIT_SHALLOW TRUE
30-
)
31-
FetchContent_GetProperties(eigen)
32-
if(NOT eigen_POPULATED)
33-
FetchContent_Populate(eigen)
34-
endif()
35-
set(EIGEN_INCLUDE_DIRS ${eigen_SOURCE_DIR})
36-
37-
install(DIRECTORY ${EIGEN_INCLUDE_DIRS}/Eigen
38-
DESTINATION include
39-
)
40-
endif()
13+
include(CPM)
14+
CPMAddPackage(
15+
NAME eigen
16+
GITLAB_REPOSITORY libeigen/eigen
17+
GIT_TAG 3.4.0
18+
DOWNLOAD_ONLY YES
19+
)
4120

4221
add_library(Eigen3_Eigen INTERFACE)
4322
add_library(Eigen3::Eigen ALIAS Eigen3_Eigen)
4423

4524
include(GNUInstallDirs)
4625
target_include_directories(Eigen3_Eigen SYSTEM INTERFACE
47-
$<BUILD_INTERFACE:${EIGEN_INCLUDE_DIRS}>
26+
$<BUILD_INTERFACE:${eigen_SOURCE_DIR}>
4827
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
4928
)
50-
# target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_MPL2_ONLY)
29+
30+
if(EIGEN_MPL2_ONLY)
31+
target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_MPL2_ONLY)
32+
endif()
33+
34+
if(EIGEN_DONT_VECTORIZE)
35+
target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_DONT_VECTORIZE)
36+
endif()
5137

5238
if(EIGEN_WITH_MKL)
5339
# TODO: Checks that, on 64bits systems, `mkl::mkl` is using the LP64 interface
@@ -58,6 +44,27 @@ if(EIGEN_WITH_MKL)
5844
EIGEN_USE_MKL_ALL
5945
EIGEN_USE_LAPACKE_STRICT
6046
)
47+
elseif(APPLE)
48+
find_package(BLAS REQUIRED)
49+
find_library(LAPACKE lapacke PATHS
50+
"/opt/local/lib/lapack"
51+
"/opt/homebrew/opt/lapack/lib"
52+
)
53+
if (NOT LAPACKE)
54+
# BLAS should be available on macOS, but LAPACKE might not be
55+
message(WARNING "LAPACKE library not found (required for EIGEN_USE_LAPACKE on macOS)! "
56+
"Perhaps you need to install it (e.g., brew install lapack). "
57+
"Eigen will be built without LAPACKE support.")
58+
else()
59+
message(STATUS "Found BLAS and LAPACKE. Enabling Eigen LAPACKE support.")
60+
target_link_libraries(Eigen3_Eigen INTERFACE
61+
${BLAS_LIBRARIES} ${LAPACKE}
62+
)
63+
target_compile_definitions(Eigen3_Eigen INTERFACE
64+
EIGEN_USE_BLAS
65+
EIGEN_USE_LAPACKE_STRICT
66+
)
67+
endif()
6168
endif()
6269

6370
# On Windows, enable natvis files to improve debugging experience
@@ -68,6 +75,6 @@ endif()
6875
# Install rules
6976
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME eigen)
7077
set_target_properties(Eigen3_Eigen PROPERTIES EXPORT_NAME Eigen)
71-
install(DIRECTORY ${EIGEN_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
78+
install(DIRECTORY ${eigen_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
7279
install(TARGETS Eigen3_Eigen EXPORT Eigen_Targets)
73-
install(EXPORT Eigen_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eigen NAMESPACE Eigen3::)
80+
install(EXPORT Eigen_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eigen NAMESPACE Eigen3::)

cmake/recipes/spdlog.cmake

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,21 @@
1-
#
2-
# Copyright 2020 Adobe. All rights reserved.
3-
# This file is licensed to you under the Apache License, Version 2.0 (the "License");
4-
# you may not use this file except in compliance with the License. You may obtain a copy
5-
# of the License at http://www.apache.org/licenses/LICENSE-2.0
6-
#
7-
# Unless required by applicable law or agreed to in writing, software distributed under
8-
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9-
# OF ANY KIND, either express or implied. See the License for the specific language
10-
# governing permissions and limitations under the License.
11-
#
1+
# spdlog (https://github.com/gabime/spdlog)
2+
# License: MIT
123
if(TARGET spdlog::spdlog)
134
return()
145
endif()
156

167
message(STATUS "Third-party: creating target 'spdlog::spdlog'")
178

18-
include(FetchContent)
19-
FetchContent_Declare(
20-
spdlog
21-
GIT_REPOSITORY https://github.com/gabime/spdlog.git
22-
GIT_TAG v1.10.0
23-
GIT_SHALLOW TRUE
24-
)
25-
269
option(SPDLOG_INSTALL "Generate the install target" ON)
2710
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "spdlog")
28-
FetchContent_MakeAvailable(spdlog)
11+
12+
include(CPM)
13+
CPMAddPackage("gh:gabime/[email protected]")
2914

3015
set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE ON)
3116

32-
set_target_properties(spdlog PROPERTIES FOLDER external)
17+
# Folder name for IDE
18+
set_target_properties(spdlog PROPERTIES FOLDER "ThirdParty")
3319

3420
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR
3521
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")

src/finitediff.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void finite_gradient(
9494

9595
void finite_jacobian(
9696
const Eigen::Ref<const Eigen::VectorXd>& x,
97-
const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& f,
97+
const std::function<Eigen::MatrixXd(const Eigen::VectorXd&)>& f,
9898
Eigen::MatrixXd& jac,
9999
const AccuracyOrder accuracy,
100100
const double eps)
@@ -107,16 +107,24 @@ void finite_jacobian(
107107

108108
const double denom = get_denominator(accuracy) * eps;
109109

110-
jac.setZero(f(x).rows(), x.rows());
110+
// Call f once to get the size of the Jacobian
111+
size_t f_rows, f_cols;
112+
{
113+
const Eigen::MatrixXd tmp = f(x);
114+
f_rows = tmp.rows(), f_cols = tmp.cols();
115+
}
116+
jac.setZero(f_rows, f_cols * x.size());
111117

118+
// f: ℝ^n ↦ ℝ^{p×q} ⟹ ∇f: ℝ^n ↦ ℝ^{p×(qn)}
112119
Eigen::VectorXd x_mutable = x;
113-
for (size_t i = 0; i < x.rows(); i++) {
120+
for (size_t i = 0; i < x.size(); i++) {
114121
for (size_t ci = 0; ci < inner_steps; ci++) {
115122
x_mutable[i] += internal_coeffs[ci] * eps;
116-
jac.col(i) += external_coeffs[ci] * f(x_mutable);
123+
jac.middleCols(f_cols * i, f_cols) +=
124+
external_coeffs[ci] * f(x_mutable);
117125
x_mutable[i] = x[i];
118126
}
119-
jac.col(i) /= denom;
127+
jac.middleCols(f_cols * i, f_cols) /= denom;
120128
}
121129
}
122130

src/finitediff.hpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,27 @@ void finite_gradient(
3939
const double eps = 1.0e-8);
4040

4141
/**
42-
* @brief Compute the jacobian of a function using finite differences.
42+
* @brief Compute the Jacobian of a function using finite differences.
4343
*
44-
* @param[in] x Point at which to compute the jacobian.
45-
* @param[in] f Compute the jacobian of this function.
46-
* @param[out] jac Computed jacobian.
44+
* @param[in] x Point at which to compute the Jacobian.
45+
* @param[in] f Compute the Jacobian of this function.
46+
* @param[out] jac Computed Jacobian.
4747
* @param[in] accuracy Accuracy of the finite differences.
4848
* @param[in] eps Value of the finite difference step.
4949
*/
5050
void finite_jacobian(
5151
const Eigen::Ref<const Eigen::VectorXd>& x,
52-
const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& f,
52+
const std::function<Eigen::MatrixXd(const Eigen::VectorXd&)>& f,
5353
Eigen::MatrixXd& jac,
5454
const AccuracyOrder accuracy = SECOND,
5555
const double eps = 1.0e-8);
5656

5757
/**
58-
* @brief Compute the hessian of a function using finite differences.
58+
* @brief Compute the Hessian of a function using finite differences.
5959
*
60-
* @param[in] x Point at which to compute the hessian.
61-
* @param[in] f Compute the hessian of this function.
62-
* @param[out] hess Computed hessian.
60+
* @param[in] x Point at which to compute the Hessian.
61+
* @param[in] f Compute the Hessian of this function.
62+
* @param[out] hess Computed Hessian.
6363
* @param[in] accuracy Accuracy of the finite differences.
6464
* @param[in] eps Value of the finite difference step.
6565
*/
@@ -87,10 +87,10 @@ bool compare_gradient(
8787
const std::string& msg = "compare_gradient ");
8888

8989
/**
90-
* @brief Compare if two jacobians are close enough.
90+
* @brief Compare if two Jacobians are close enough.
9191
*
92-
* @param[in] x The first jacobian to compare.
93-
* @param[in] y The second jacobian to compare against.
92+
* @param[in] x The first Jacobian to compare.
93+
* @param[in] y The second Jacobian to compare against.
9494
* @param[in] test_eps Tolerance of equality.
9595
* @param[in] msg Debug message header.
9696
*
@@ -103,10 +103,10 @@ bool compare_jacobian(
103103
const std::string& msg = "compare_jacobian ");
104104

105105
/**
106-
* @brief Compare if two hessians are close enough.
106+
* @brief Compare if two Hessians are close enough.
107107
*
108-
* @param[in] x The first hessian to compare.
109-
* @param[in] y The second hessian to compare against.
108+
* @param[in] x The first Hessian to compare.
109+
* @param[in] y The second Hessian to compare against.
110110
* @param[in] test_eps Tolerance of equality.
111111
* @param[in] msg Debug message header.
112112
*

0 commit comments

Comments
 (0)