tinyopt 700
Tinyopt, a lightweight, header only optimization library
Loading...
Searching...
No Matches
suitesparse.h
1// Copyright 2026 Julien Michot.
2// SPDX-License-Identifier: Apache-2.0
3
4#pragma once
5
6#if defined(TINYOPT_ENABLE_SUITESPARSE)
7
8#include <memory>
9#include <optional>
10#include <type_traits>
11
12#include <cholmod.h>
13
14#include <tinyopt/types.h>
15
16namespace tinyopt::suitesparse_detail {
17
18struct Common {
19 cholmod_common value{};
20 bool started = cholmod_start(&value) != 0;
21
22 ~Common() {
23 if (started) cholmod_finish(&value);
24 }
25};
26
27struct FactorDeleter {
28 cholmod_common *common;
29
30 void operator()(cholmod_factor *factor) const {
31 if (factor) cholmod_free_factor(&factor, common);
32 }
33};
34
35struct DenseDeleter {
36 cholmod_common *common;
37
38 void operator()(cholmod_dense *dense) const {
39 if (dense) cholmod_free_dense(&dense, common);
40 }
41};
42
43template <typename Scalar, int RowsAtCompileTime>
44std::optional<Vector<Scalar, RowsAtCompileTime>> SolveWithView(
45 const SparseMatrix<Scalar> &A, const Vector<Scalar, RowsAtCompileTime> &b) {
46 if (A.rows() != A.cols() || A.rows() != b.size() || A.rows() == 0) return std::nullopt;
47
48 static_assert(std::is_same_v<Scalar, double> || std::is_same_v<Scalar, float>);
49 static_assert(std::is_same_v<typename SparseMatrix<Scalar>::StorageIndex, int>);
50
51 Common context;
52 if (!context.started) return std::nullopt;
53 context.value.print = 0;
54
55 cholmod_sparse matrix{};
56 matrix.nrow = static_cast<size_t>(A.rows());
57 matrix.ncol = static_cast<size_t>(A.cols());
58 matrix.nzmax = static_cast<size_t>(A.data().allocatedSize());
59 matrix.p = const_cast<int *>(A.outerIndexPtr());
60 matrix.i = const_cast<int *>(A.innerIndexPtr());
61 matrix.x = const_cast<Scalar *>(A.valuePtr());
62 matrix.nz = A.isCompressed() ? nullptr : const_cast<int *>(A.innerNonZeroPtr());
63 matrix.stype = 1;
64 matrix.itype = CHOLMOD_INT;
65 matrix.xtype = CHOLMOD_REAL;
66 matrix.dtype = std::is_same_v<Scalar, double> ? CHOLMOD_DOUBLE : CHOLMOD_SINGLE;
67 matrix.sorted = 1;
68 matrix.packed = A.isCompressed();
69
70 std::unique_ptr<cholmod_factor, FactorDeleter> factor(
71 cholmod_analyze(&matrix, &context.value), FactorDeleter{&context.value});
72 if (!factor || !cholmod_factorize(&matrix, factor.get(), &context.value) ||
73 factor->minor != matrix.ncol)
74 return std::nullopt;
75
76 cholmod_dense rhs{};
77 rhs.nrow = static_cast<size_t>(b.size());
78 rhs.ncol = 1;
79 rhs.nzmax = static_cast<size_t>(b.size());
80 rhs.d = rhs.nrow;
81 rhs.x = const_cast<Scalar *>(b.data());
82 rhs.xtype = CHOLMOD_REAL;
83 rhs.dtype = matrix.dtype;
84
85 std::unique_ptr<cholmod_dense, DenseDeleter> result(
86 cholmod_solve(CHOLMOD_A, factor.get(), &rhs, &context.value),
87 DenseDeleter{&context.value});
88 if (!result) return std::nullopt;
89
90 using Result = Vector<Scalar, RowsAtCompileTime>;
91 Result solution(b.size());
92 Eigen::Map<const Vector<Scalar>> result_view(static_cast<const Scalar *>(result->x), b.size());
93 solution = result_view;
94 if (!solution.allFinite()) return std::nullopt;
95 return solution;
96}
97
98template <typename Scalar, int RowsAtCompileTime>
99std::optional<Vector<Scalar, RowsAtCompileTime>> Solve(
100 const SparseMatrix<Scalar> &A, const Vector<Scalar, RowsAtCompileTime> &b) {
101 return SolveWithView<Scalar, RowsAtCompileTime>(A, b);
102}
103
104} // namespace tinyopt::suitesparse_detail
105
106#endif