Skip to content

Commit beef586

Browse files
authored
Merge pull request #3 from Simple-Robotics/topic/pixi
Add pixi support, CI, and fixup testing
2 parents 84d9bb7 + 4c32f02 commit beef586

File tree

19 files changed

+4176
-214
lines changed

19 files changed

+4176
-214
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SCM syntax highlighting & preventing 3-way merges
2+
pixi.lock merge=binary linguist-language=YAML linguist-generated=true
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: CI - MacOS/Linux/Windows via Pixi
2+
3+
on:
4+
push:
5+
paths-ignore:
6+
- .gitlab-ci.yml
7+
- .gitignore
8+
- '*.md'
9+
- CITATION.*
10+
- LICENSE
11+
- colcon.pkg
12+
- .pre-commit-config.yaml
13+
- CHANGELOG.md
14+
- development/*.md
15+
pull_request:
16+
paths-ignore:
17+
- .gitlab-ci.yml
18+
- .gitignore
19+
- '*.md'
20+
- CITATION.*
21+
- LICENSE
22+
- colcon.pkg
23+
- .pre-commit-config.yaml
24+
- CHANGELOG.md
25+
- development/*.md
26+
concurrency:
27+
group: ${{ github.workflow }}-${{ github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
nanoeigenpy-pixi:
32+
name: ${{ matrix.os }} - Env ${{ matrix.environment }} ${{ matrix.build_type }}
33+
runs-on: ${{ matrix.os }}
34+
env:
35+
CCACHE_BASEDIR: "${GITHUB_WORKSPACE}"
36+
CCACHE_DIR: "${GITHUB_WORKSPACE}/.ccache"
37+
CCACHE_COMPRESS: true
38+
CCACHE_COMPRESSLEVEL: 6
39+
40+
strategy:
41+
fail-fast: false
42+
matrix:
43+
os: [ubuntu-latest, macos-latest, macos-13, windows-latest]
44+
environment: [default, python-oldest]
45+
build_type: [Release, Debug]
46+
47+
steps:
48+
- uses: actions/checkout@v4
49+
with:
50+
submodules: recursive
51+
52+
- uses: actions/cache@v4
53+
with:
54+
path: .ccache
55+
key: ccache-macos-linux-windows-pixi-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.environment }}-${{ github.sha }}
56+
restore-keys: ccache-macos-linux-windows-pixi-${{ matrix.os }}-${{ matrix.build_type }}-${{ matrix.environment }}-
57+
58+
- uses: prefix-dev/[email protected]
59+
with:
60+
cache: true
61+
environments: ${{ matrix.environment }}
62+
63+
- name: Build nanoeigenpy [MacOS/Linux/Windows]
64+
env:
65+
NANOEIGENPY_BUILD_TYPE: ${{ matrix.build_type }}
66+
run: |
67+
pixi run -e ${{ matrix.environment }} build
68+
69+
- name: Test nanoeigenpy [MacOS/Linux/Windows]
70+
run: |
71+
pixi run -e ${{ matrix.environment }} ctest --test-dir build --output-on-failure
72+
73+
check:
74+
if: always()
75+
name: check-macos-linux-windows-pixi
76+
77+
needs:
78+
- nanoeigenpy-pixi
79+
80+
runs-on: Ubuntu-latest
81+
82+
steps:
83+
- name: Decide whether the needed jobs succeeded or failed
84+
uses: re-actors/alls-green@release/v1
85+
with:
86+
jobs: ${{ toJSON(needs) }}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
build*/
22
.cache/
3+
4+
# pixi environments
5+
.pixi
6+
*.egg-info

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ include(cmake/base.cmake)
1111
COMPUTE_PROJECT_ARGS(PROJECT_ARGS LANGUAGES CXX)
1212
project(${PROJECT_NAME} ${PROJECT_ARGS})
1313

14+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
1415
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
16+
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
1517

1618
find_package(Eigen3 REQUIRED)
1719

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
:: Setup ccache
2+
set CMAKE_CXX_COMPILER_LAUNCHER=ccache
3+
4+
:: Create compile_commands.json for language server
5+
set CMAKE_EXPORT_COMPILE_COMMANDS=1
6+
7+
:: Activate color output with Ninja
8+
set CMAKE_COLOR_DIAGNOSTICS=1
9+
10+
:: Set default build value only if not previously set
11+
if not defined NANOEIGENPY_BUILD_TYPE (set NANOEIGENPY_BUILD_TYPE=Release)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#! /bin/bash
2+
# Activation script
3+
4+
# Remove flags setup from cxx-compiler
5+
unset CFLAGS
6+
unset CPPFLAGS
7+
unset CXXFLAGS
8+
unset DEBUG_CFLAGS
9+
unset DEBUG_CPPFLAGS
10+
unset DEBUG_CXXFLAGS
11+
unset LDFLAGS
12+
13+
if [[ $host_alias == *"apple"* ]];
14+
then
15+
# On OSX setting the rpath and -L it's important to use the conda libc++ instead of the system one.
16+
# If conda-forge use install_name_tool to package some libs, -headerpad_max_install_names is then mandatory
17+
export LDFLAGS="-Wl,-headerpad_max_install_names -Wl,-rpath,$CONDA_PREFIX/lib -L$CONDA_PREFIX/lib"
18+
elif [[ $host_alias == *"linux"* ]];
19+
then
20+
# On GNU/Linux, I don't know if these flags are mandatory with g++ but
21+
# it allow to use clang++ as compiler
22+
export LDFLAGS="-Wl,-rpath,$CONDA_PREFIX/lib -Wl,-rpath-link,$CONDA_PREFIX/lib -L$CONDA_PREFIX/lib"
23+
fi
24+
25+
# Setup ccache
26+
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
27+
28+
# Create compile_commands.json for language server
29+
export CMAKE_EXPORT_COMPILE_COMMANDS=1
30+
31+
# Activate color output with Ninja
32+
export CMAKE_COLOR_DIAGNOSTICS=1
33+
34+
# Set default build value only if not previously set
35+
export NANOEIGENPY_BUILD_TYPE=${NANOEIGENPY_BUILD_TYPE:=Release}

include/nanoeigenpy/decompositions/ldlt.hpp

Lines changed: 115 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -7,123 +7,144 @@ namespace nanoeigenpy {
77
namespace nb = nanobind;
88

99
template <typename MatrixType, typename MatrixOrVector>
10-
MatrixOrVector solve(const Eigen::LDLT<MatrixType> &c, const MatrixOrVector &vec) {
11-
return c.solve(vec);
10+
MatrixOrVector solve(const Eigen::LDLT<MatrixType> &c,
11+
const MatrixOrVector &vec) {
12+
return c.solve(vec);
1213
}
1314

1415
template <typename MatrixType>
1516
void exposeLDLTSolver(nb::module_ m, const char *name) {
1617
using Solver = Eigen::LDLT<MatrixType>;
1718
using Scalar = typename MatrixType::Scalar;
1819
using VectorType = Eigen::Matrix<Scalar, -1, 1>;
19-
auto cl = nb::class_<Solver>(m, name,
20-
"Robust Cholesky decomposition of a matrix with pivoting.\n\n"
21-
"Perform a robust Cholesky decomposition of a positive semidefinite or "
22-
"negative semidefinite matrix $ A $ such that $ A = P^TLDL^*P $, where "
23-
"P is a permutation matrix, L is lower triangular with a unit diagonal "
24-
"and D is a diagonal matrix.\n\n"
25-
"The decomposition uses pivoting to ensure stability, so that L will "
26-
"have zeros in the bottom right rank(A) - n submatrix. Avoiding the "
27-
"square root on D also stabilizes the computation.")
28-
29-
.def(nb::init<>(),
30-
"Default constructor.")
31-
.def(nb::init<Eigen::DenseIndex>(), nb::arg("size"),
32-
"Default constructor with memory preallocation.")
33-
.def(nb::init<const MatrixType &>(), nb::arg("matrix"),
34-
"Constructs a LLT factorization from a given matrix.")
35-
36-
.def(EigenBaseVisitor())
37-
38-
.def("isNegative", &Solver::isNegative,
39-
"Returns true if the matrix is negative (semidefinite).")
40-
.def("isPositive", &Solver::isPositive,
41-
"Returns true if the matrix is positive (semidefinite).")
42-
43-
.def("matrixL",
44-
[](Solver const &c) -> MatrixType { return c.matrixL(); },
45-
"Returns the lower triangular matrix L.")
46-
.def("matrixU",
47-
[](Solver const &c) -> MatrixType { return c.matrixU(); },
48-
"Returns the upper triangular matrix U.")
49-
.def("vectorD",
50-
[](Solver const &c) -> VectorType { return c.vectorD(); },
51-
"Returns the coefficients of the diagonal matrix D.")
52-
.def("matrixLDLT", &Solver::matrixLDLT,
53-
"Returns the LDLT decomposition matrix.",
54-
nb::rv_policy::reference_internal)
55-
56-
.def("transpositionsP",
57-
[](Solver const &c) -> MatrixType { return c.transpositionsP() *
58-
MatrixType::Identity(c.matrixL().rows(), c.matrixL().rows()); },
59-
"Returns the permutation matrix P.")
60-
61-
.def("rankUpdate",
62-
[](Solver &c, VectorType const &w, Scalar sigma) {
63-
return c.rankUpdate(w, sigma);
64-
},
65-
nb::arg("w"), nb::arg("sigma"))
20+
auto cl =
21+
nb::class_<Solver>(
22+
m, name,
23+
"Robust Cholesky decomposition of a matrix with pivoting.\n\n"
24+
"Perform a robust Cholesky decomposition of a positive semidefinite "
25+
"or "
26+
"negative semidefinite matrix $ A $ such that $ A = P^TLDL^*P $, "
27+
"where "
28+
"P is a permutation matrix, L is lower triangular with a unit "
29+
"diagonal "
30+
"and D is a diagonal matrix.\n\n"
31+
"The decomposition uses pivoting to ensure stability, so that L will "
32+
"have zeros in the bottom right rank(A) - n submatrix. Avoiding the "
33+
"square root on D also stabilizes the computation.")
34+
35+
.def(nb::init<>(), "Default constructor.")
36+
.def(nb::init<Eigen::DenseIndex>(), nb::arg("size"),
37+
"Default constructor with memory preallocation.")
38+
.def(nb::init<const MatrixType &>(), nb::arg("matrix"),
39+
"Constructs a LLT factorization from a given matrix.")
40+
41+
.def(EigenBaseVisitor())
42+
43+
.def("isNegative", &Solver::isNegative,
44+
"Returns true if the matrix is negative (semidefinite).")
45+
.def("isPositive", &Solver::isPositive,
46+
"Returns true if the matrix is positive (semidefinite).")
47+
48+
.def(
49+
"matrixL",
50+
[](Solver const &c) -> MatrixType { return c.matrixL(); },
51+
"Returns the lower triangular matrix L.")
52+
.def(
53+
"matrixU",
54+
[](Solver const &c) -> MatrixType { return c.matrixU(); },
55+
"Returns the upper triangular matrix U.")
56+
.def(
57+
"vectorD",
58+
[](Solver const &c) -> VectorType { return c.vectorD(); },
59+
"Returns the coefficients of the diagonal matrix D.")
60+
.def("matrixLDLT", &Solver::matrixLDLT,
61+
"Returns the LDLT decomposition matrix.",
62+
nb::rv_policy::reference_internal)
63+
64+
.def(
65+
"transpositionsP",
66+
[](Solver const &c) -> MatrixType {
67+
return c.transpositionsP() *
68+
MatrixType::Identity(c.matrixL().rows(),
69+
c.matrixL().rows());
70+
},
71+
"Returns the permutation matrix P.")
72+
73+
.def(
74+
"rankUpdate",
75+
[](Solver &c, VectorType const &w, Scalar sigma) {
76+
return c.rankUpdate(w, sigma);
77+
},
78+
nb::arg("w"), nb::arg("sigma"))
6679

6780
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
68-
.def("adjoint", &Solver::adjoint,
69-
"Returns the adjoint, that is, a reference to the decomposition "
70-
"itself as if the underlying matrix is self-adjoint.",
71-
nb::rv_policy::reference)
81+
.def("adjoint", &Solver::adjoint,
82+
"Returns the adjoint, that is, a reference to the decomposition "
83+
"itself as if the underlying matrix is self-adjoint.",
84+
nb::rv_policy::reference)
7285
#endif
7386

74-
.def("compute",
75-
[](Solver &c, VectorType const &matrix) {
76-
return c.compute(matrix);
77-
},
78-
nb::arg("matrix"),
79-
"Computes the LDLT of given matrix.",
80-
nb::rv_policy::reference)
81-
.def("info", &Solver::info,
82-
"NumericalIssue if the input contains INF or NaN values or "
83-
"overflow occured. Returns Success otherwise.")
87+
.def(
88+
"compute",
89+
[](Solver &c, VectorType const &matrix) {
90+
return c.compute(matrix);
91+
},
92+
nb::arg("matrix"), "Computes the LDLT of given matrix.",
93+
nb::rv_policy::reference)
94+
.def("info", &Solver::info,
95+
"NumericalIssue if the input contains INF or NaN values or "
96+
"overflow occured. Returns Success otherwise.")
8497

8598
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
86-
.def("rcond", &Solver::rcond,
87-
"Returns an estimate of the reciprocal condition number of the "
88-
"matrix.")
99+
.def("rcond", &Solver::rcond,
100+
"Returns an estimate of the reciprocal condition number of the "
101+
"matrix.")
89102
#endif
90103

91-
.def("reconstructedMatrix", &Solver::reconstructedMatrix,
92-
"Returns the matrix represented by the decomposition, i.e., it "
93-
"returns the product: L L^*. This function is provided for debug "
94-
"purpose.")
95-
96-
.def("solve",
97-
[](Solver const &c, VectorType const &b) -> VectorType { return solve(c, b); },
98-
nb::arg("b"),
99-
"Returns the solution x of A x = b using the current "
100-
"decomposition of A.")
101-
.def("solve",
102-
[](Solver const &c, MatrixType const &B) -> MatrixType { return solve(c, B); },
103-
nb::arg("B"),
104-
"Returns the solution X of A X = B using the current "
105-
"decomposition of A where B is a right hand side matrix.")
106-
107-
.def("setZero", &Solver::setZero,
108-
"Clear any existing decomposition.")
109-
110-
.def("id",
111-
[](Solver const &c) -> int64_t { return reinterpret_cast<int64_t>(&c); },
112-
"Returns the unique identity of an object.\n"
113-
"For objects held in C++, it corresponds to its memory address.");
114-
104+
.def(
105+
"reconstructedMatrix", &Solver::reconstructedMatrix,
106+
"Returns the matrix represented by the decomposition, i.e., it "
107+
"returns the product: L L^*. This function is provided for debug "
108+
"purpose.")
109+
110+
.def(
111+
"solve",
112+
[](Solver const &c, VectorType const &b) -> VectorType {
113+
return solve(c, b);
114+
},
115+
nb::arg("b"),
116+
"Returns the solution x of A x = b using the current "
117+
"decomposition of A.")
118+
.def(
119+
"solve",
120+
[](Solver const &c, MatrixType const &B) -> MatrixType {
121+
return solve(c, B);
122+
},
123+
nb::arg("B"),
124+
"Returns the solution X of A X = B using the current "
125+
"decomposition of A where B is a right hand side matrix.")
126+
127+
.def("setZero", &Solver::setZero, "Clear any existing decomposition.")
128+
129+
.def(
130+
"id",
131+
[](Solver const &c) -> int64_t {
132+
return reinterpret_cast<int64_t>(&c);
133+
},
134+
"Returns the unique identity of an object.\n"
135+
"For objects held in C++, it corresponds to its memory address.");
115136
}
116137

117138
} // namespace nanoeigenpy
118139

119-
120140
// TODO
121141

122-
// Tests that were not done in eigenpy that we could add in nanoeigenpy: (+ those cited in llt.hpp)
123-
// setZero
142+
// Tests that were not done in eigenpy that we could add in nanoeigenpy: (+
143+
// those cited in llt.hpp) setZero
124144

125145
// Expose supplementary content:
126146
// setZero in LLT decomp too ? (not done in eigenpy)
127147

128148
// Questions about eigenpy itself:
129-
// Relevant to have the reconstructedMatrix method ? (knowing that we are on LDLT, not LLT)
149+
// Relevant to have the reconstructedMatrix method ? (knowing that we are on
150+
// LDLT, not LLT)

0 commit comments

Comments
 (0)