packages.biu: 完成 eigen 转换

This commit is contained in:
2024-08-22 20:10:04 +08:00
parent aa026f332e
commit 5dd03880c1
5 changed files with 171 additions and 7 deletions

View File

@@ -162,7 +162,7 @@
config = { archive = false; branch = "production"; };
devShells.x86_64-linux = let inherit (inputs.self.packages.x86_64-linux) pkgs; in
{
biu = pkgs.mkShell
biu = pkgs.mkShell.override { stdenv = pkgs.gcc14Stdenv; }
{
inputsFrom = [ pkgs.localPackages.biu ];
packages = [ pkgs.clang-tools_18 ];

View File

@@ -32,10 +32,12 @@ namespace biu
// if size is specified as a number, convert to fixed-size Eigen::Vector if specified size equals the size of the
// input, otherwise throw an error
// return deduced size if the size is deducible in compile time, otherwise return Empty
template <std::size_t ToSize, typename Container> constexpr auto deduce_eigen_size();
template <std::size_t ToSize, typename Container> consteval auto deduce_eigen_size();
// helper operator| to specify the size of the destination container
template <std::size_t Row, std::size_t Col> struct ToEigenHelper {};
template <std::size_t Size> struct FromEigenVectorHelper {};
template <std::size_t Row, std::size_t Col> struct FromEigenMatrixHelper {};
// convert 1D standard container to Eigen::Matrix, the second argument should always be unspecified
template <typename From, std::size_t ToSize> auto operator|
@@ -51,11 +53,23 @@ namespace biu
&& detail_::StandardContainer<typename From::value_type, typename From::value_type::value_type>
&& Arithmetic<typename From::value_type::value_type>
);
// convert 1D Eigen matrix to std::vector or std::array
template <typename Vector, std::size_t ToSize> auto operator|
(const Vector&, const detail_::FromEigenVectorHelper<ToSize>&);
// convert 2D Eigen matrix to std::vector or std::array
template <typename Matrix, std::size_t ToRow, std::size_t ToCol> auto operator|
(const Matrix&, const detail_::FromEigenMatrixHelper<ToRow, ToCol>&);
}
// usage: some_value | toEigen<Row, Col>
template <std::size_t Row = detail_::unspecifiedSize, std::size_t Col = detail_::unspecifiedSize>
inline constexpr detail_::ToEigenHelper<Row, Col> toEigen;
template <std::size_t Size = detail_::unspecifiedSize>
inline constexpr detail_::FromEigenVectorHelper<Size> fromEigenVector;
template <std::size_t Row = detail_::unspecifiedSize, std::size_t Col = detail_::unspecifiedSize>
inline constexpr detail_::FromEigenMatrixHelper<Row, Col> fromEigenMatrix;
// test if a class is an eigen matrix
namespace detail_
@@ -66,7 +80,7 @@ namespace biu
}
template <typename Matrix> concept EigenMatrix = detail_::EigenMatrix<Matrix>::value;
}
using eigen::toEigen, eigen::EigenMatrix;
using eigen::toEigen, eigen::fromEigenVector, eigen::fromEigenMatrix, eigen::EigenMatrix;
}
// archive a matrix

View File

@@ -1,4 +1,5 @@
# pragma once
# include <utility>
# include <biu/eigen.hpp>
# include <biu/common.hpp>
# include <range/v3/view.hpp>
@@ -6,10 +7,10 @@
namespace biu::eigen
{
template <std::size_t ToSize, typename Container> constexpr auto detail_::deduce_eigen_size()
template <std::size_t ToSize, typename Container> consteval auto detail_::deduce_eigen_size()
{
if constexpr (ToSize == detail_::dynamicSize) return Empty{};
else if constexpr (ToSize == detail_::unspecifiedSize)
if constexpr (ToSize == dynamicSize) return Empty{};
else if constexpr (ToSize == unspecifiedSize)
if constexpr (SpecializationOfArray<Container>) return Container{}.size();
else return Empty{};
else
@@ -105,6 +106,150 @@ namespace biu::eigen
return Matrix(Eigen::Map<const Matrix>(data.data()));
}
}
template <typename Vector, std::size_t ToSize> auto detail_::operator|
(const Vector& vector, const detail_::FromEigenVectorHelper<ToSize>&)
{
// 尽量在编译时获得大小并检查大小是否匹配,第一个返回值为确定的大小,第二个返回值为用于在运行时检查大小的函数
auto get_size = []<std::size_t to_size>(this auto&& self) consteval
{
// 如果没有指定
if constexpr (to_size == unspecifiedSize)
// 如果两个维度都是动态的,那么作为动态大小处理
if constexpr
(
Vector::CompileTimeTraits::RowsAtCompileTime == Eigen::Dynamic
&& Vector::CompileTimeTraits::ColsAtCompileTime == Eigen::Dynamic
)
return std::make_pair
(dynamicSize, [](const Vector& vector) { return vector.nrows() <= 1 && vector.ncols() <= 1; });
// 如果两个维度都是固定的
else if constexpr
(
Vector::CompileTimeTraits::RowsAtCompileTime != Eigen::Dynamic
&& Vector::CompileTimeTraits::ColsAtCompileTime != Eigen::Dynamic
)
// 如果两个维度都超过了 1
if constexpr
(Vector::CompileTimeTraits::RowsAtCompileTime > 1 && Vector::CompileTimeTraits::ColsAtCompileTime > 1)
throw std::invalid_argument("The size of the destination Eigen container mismatches the input container");
// 否则返回两个维度的乘积
else return std::make_pair
(
Vector::CompileTimeTraits::RowsAtCompileTime * Vector::CompileTimeTraits::ColsAtCompileTime,
[](const Vector&) consteval { return true; }
);
// 如果固定的那个维度等于 1那么为动态大小大小取决于另外一个没有固定的维度
// 否则,大小等于这个维度,另一个维度是否为 1 留作之后检查
else if constexpr (Vector::CompileTimeTraits::RowsAtCompileTime != Eigen::Dynamic)
if constexpr (Vector::CompileTimeTraits::RowsAtCompileTime == 1)
return std::make_pair(dynamicSize, [](const Vector&) consteval { return true; });
else
return std::make_pair
(
Vector::CompileTimeTraits::RowsAtCompileTime,
[](const Vector& vector) { return vector.ncols() <= 1; }
);
else if constexpr (Vector::CompileTimeTraits::ColsAtCompileTime != Eigen::Dynamic)
if constexpr (Vector::CompileTimeTraits::ColsAtCompileTime == 1)
return std::make_pair(dynamicSize, [](const Vector&) consteval { return true; });
else
return std::make_pair
(
Vector::CompileTimeTraits::ColsAtCompileTime,
[](const Vector& vector) { return vector.nrows() <= 1; }
);
else
std::unreachable();
// 如果指定了为动态:同样按照上述检查,但返回动态大小
else if constexpr (to_size == dynamicSize)
return std::make_pair(dynamicSize, self->template operator()<unspecifiedSize>().second);
// 如果指定了大小:按照上述检查,如果判断为静态大小且大小不一致则报错,如果判断为动态大小则额外判断大小
else
{
auto result = self->template operator()<unspecifiedSize>();
if constexpr (result.first != dynamicSize)
if constexpr (result.first != to_size)
throw std::invalid_argument("The size of the destination Eigen container mismatches the input container");
else
return result;
else
return std::make_pair
(
to_size,
[size = to_size](const Vector& vector) { return vector.size() == size; }
);
}
};
// decomposition declarations can't be constexpr
constexpr auto size = get_size.template operator()<ToSize>();
if (!size.second(vector))
throw std::invalid_argument("The size of the destination Eigen container mismatches the input container");
if constexpr (size.first == dynamicSize)
return std::vector<typename Vector::Scalar>(vector.data(), vector.data() + vector.size());
else
return std::array<typename Vector::Scalar, size.first>(vector.data(), vector.data() + vector.size());
}
template <typename Matrix, std::size_t ToRow, std::size_t ToCol> auto detail_::operator|
(const Matrix& matrix, const detail_::FromEigenMatrixHelper<ToRow, ToCol>&)
{
auto get_size = [] consteval
{
auto get_one_size = []<std::size_t to_size, int eigen_size> consteval
{
// 如果没有指定
if constexpr (to_size == unspecifiedSize)
// 如果原大小是动态的,那么作为动态大小处理
if constexpr (eigen_size == Eigen::Dynamic)
return std::make_pair(dynamicSize, [](int) { return true; });
// 否则返回原大小
else return std::make_pair(eigen_size, [](int) { return true; });
// 如果指定为动态大小:直接返回动态大小
else if constexpr (to_size == dynamicSize)
return std::make_pair(dynamicSize, [](int) { return true; });
// 如果指定了大小:如果原大小是动态的则返回指定的大小,并在稍后检查;否则现在检查并返回
else
if constexpr (eigen_size == Eigen::Dynamic)
return std::make_pair(to_size, [](int original_size) { return to_size == original_size; });
else
if constexpr (to_size == eigen_size)
return std::make_pair(to_size, [](int) { return true; });
else
throw std::invalid_argument("The size of the destination Eigen container mismatches the input container");
};
constexpr auto row = get_one_size.template operator()<ToRow, Matrix::CompileTimeTraits::RowsAtCompileTime>();
constexpr auto col = get_one_size.template operator()<ToCol, Matrix::CompileTimeTraits::ColsAtCompileTime>();
return std::make_pair
(
std::make_pair(row.first, col.first),
[row_check = row.second, col_check = col.second](const Matrix& matrix)
{ return row_check(matrix.rows()) && col_check(matrix.cols()); }
);
};
// decomposition declarations can't be constexpr
constexpr auto size = get_size();
if (!size.second(matrix))
throw std::invalid_argument("The size of the destination Eigen container mismatches the input container");
// 首先构造每一行
using container_per_row = std::conditional_t<size.first.second == dynamicSize,
std::vector<typename Matrix::Scalar>, std::array<typename Matrix::Scalar, size.first.second>>;
using container = std::conditional_t<size.first.first == dynamicSize,
std::vector<container_per_row>, std::array<container_per_row, size.first.first>>;
container result;
if constexpr (size.first.first == dynamicSize) result.resize(matrix.rows());
if constexpr (size.first.second == dynamicSize) for (auto& row : result) row.resize(matrix.cols());
for (int i = 0; i < matrix.rows(); i++)
{
using RowVector = Eigen::RowVector
<typename Matrix::Scalar, size.first.second == dynamicSize ? Eigen::Dynamic : size.first.second>;
Eigen::Map<RowVector>(result[i].data(), 1, matrix.cols()) = matrix.row(i);
}
return result;
}
}
template <typename Matrix> constexpr auto Eigen::serialize(auto & archive, Matrix& matrix)
requires biu::EigenMatrix<std::remove_cvref_t<Matrix>>

View File

@@ -15,6 +15,11 @@ int main()
auto d = std::array{std::array{1, 2}, std::array{3, 4}, std::array{5, 6}}
| biu::eigen::toEigen<>;
static_assert(std::same_as<decltype(d), Eigen::Matrix<int, 3, 2, Eigen::RowMajor | Eigen::AutoAlign>>);
auto f = std::vector{1, 2, 3, 4, 5};
assert(f == (f | biu::toEigen<> | biu::fromEigenVector<>));
auto g = std::vector
{std::array{1, 2}, std::array{3, 4}, std::array{5, 6}};
assert(g == (g | biu::toEigen<> | biu::fromEigenMatrix<>));
auto e = biu::deserialize<decltype(c)>(biu::serialize(c));
static_assert(std::same_as<decltype(e), decltype(c)>);

View File

@@ -58,7 +58,7 @@ inputs: rec
mumax = inputs.pkgs.callPackage ./mumax.nix { src = inputs.topInputs.mumax; };
kylin-virtual-keyboard = inputs.pkgs.libsForQt5.callPackage ./kylin-virtual-keyboard.nix
{ src = inputs.topInputs.kylin-virtual-keyboard; };
biu = inputs.pkgs.callPackage ./biu { inherit nameof zpp-bits; };
biu = inputs.pkgs.callPackage ./biu { inherit nameof zpp-bits; stdenv = inputs.pkgs.gcc14Stdenv; };
zxorm = inputs.pkgs.callPackage ./zxorm.nix { src = inputs.topInputs.zxorm; };
hpcstat = inputs.pkgs.callPackage ./hpcstat
{ inherit sqlite-orm date biu openxlsx; stdenv = inputs.pkgs.gcc14Stdenv; };