init: 初始化krylov复现项目

This commit is contained in:
M4yGem1ni
2026-04-09 19:38:56 +08:00
commit 173ad1cf2f
13 changed files with 518 additions and 0 deletions

58
.gitignore vendored Normal file
View File

@@ -0,0 +1,58 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
venv/
ENV/
env/
.venv
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Testing
.pytest_cache/
.coverage
htmlcov/
# Logs
*.log
# Environment variables
.env
.env.local
# OS
Thumbs.db
.DS_Store

78
CMakeLists.txt Normal file
View File

@@ -0,0 +1,78 @@
cmake_minimum_required(VERSION 3.18)
project(krylov_gmres VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
option(KRYLOV_BUILD_TESTS "Build krylov unit tests" ON)
# 避免 Eigen / spdlog 把自己的测试注册进 CTest
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(EIGEN_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(EIGEN_BUILD_DOC OFF CACHE BOOL "" FORCE)
# ---------------- 第三方依赖 ----------------
include(FetchContent)
FetchContent_Declare(
eigen
GIT_REPOSITORY https://aliyun.mayblog.top/devtools/eigen.git
GIT_TAG 3.4.0
)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://aliyun.mayblog.top/devtools/spdlog.git
GIT_TAG v1.11.0
)
# Eigen 是 header-only避免 add_subdirectory 把其庞大测试集带进 CTest
FetchContent_GetProperties(eigen)
if(NOT eigen_POPULATED)
FetchContent_Populate(eigen)
endif()
add_library(Eigen3_Eigen INTERFACE)
target_include_directories(Eigen3_Eigen INTERFACE ${eigen_SOURCE_DIR})
add_library(Eigen3::Eigen ALIAS Eigen3_Eigen)
FetchContent_MakeAvailable(spdlog)
# ---------------- 通用 logger 库(与 krylov 解耦,可独立复用) ----------------
add_library(logger STATIC
src/logger/logger.cpp
)
target_include_directories(logger PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(logger PUBLIC spdlog::spdlog)
# ---------------- Krylov 核心库 ----------------
# 算法实现arnoldi / gmres ...)集中到 krylov_core。
# 目前仅依赖 Eigen 与通用 logger。
add_library(krylov_core INTERFACE)
target_include_directories(krylov_core INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(krylov_core INTERFACE
Eigen3::Eigen
logger
)
add_library(krylov::core ALIAS krylov_core)
# ---------------- 可执行文件 ----------------
add_executable(krylov_solver src/main.cpp)
target_link_libraries(krylov_solver PRIVATE krylov::core)
# ---------------- 测试 ----------------
if(KRYLOV_BUILD_TESTS)
enable_testing()
add_executable(test_logger tests/test_logger.cpp)
target_link_libraries(test_logger PRIVATE krylov::core)
add_test(NAME test_logger COMMAND test_logger)
add_executable(test_eigen tests/test_eigen.cpp)
target_link_libraries(test_eigen PRIVATE krylov::core)
add_test(NAME test_eigen COMMAND test_eigen)
endif()

0
README.md Normal file
View File

0
data/.gitkeep Normal file
View File

View File

@@ -0,0 +1 @@
#pragma once

1
include/krylov/gmres.hpp Normal file
View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,31 @@
#pragma once
// Krylov 项目公共数值类型别名。
//
// 约定:算法代码只使用本文件中的别名,不直接写裸 Eigen 类型。
// 后续若要切换精度 (float/double)、索引宽度或矩阵存储布局,
// 只需改动此文件,无需触碰算法实现。
//
// 规模变大后可拆分为:
// - krylov/types/dense.hpp
// - krylov/types/sparse.hpp
// - krylov/types/traits.hpp
#include <Eigen/Dense>
#include <Eigen/Sparse>
namespace krylov {
// 标量与索引
using Scalar = double;
using Index = Eigen::Index;
// 稠密
using Vector = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
using Matrix = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
// 稀疏(行主序,方便按行遍历 SpMV
using SparseMatrix = Eigen::SparseMatrix<Scalar, Eigen::RowMajor, Index>;
using Triplet = Eigen::Triplet<Scalar, Index>;
} // namespace krylov

69
include/logger/logger.hpp Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/details/thread_pool.h> // 新增引入thread_pool所在头文件
#include <memory>
#include <string>
#include <vector> // 新增用于sink向量转换避免编译报错
// 单例模式封装 logger全局唯一支持多模块、局部级别、异步输出
class Logger {
public:
// 禁止拷贝构造和赋值运算符(单例)
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
// 初始化 logger必须在程序启动时调用建议main函数开头
// 参数:日志文件路径、日志文件最大大小(MB)、备份文件数量、是否开启异步
static void Init(const std::string& log_path = "logs/app.log",
size_t max_file_size_mb = 5,
size_t max_backup_count = 3,
bool is_async = true);
// 获取全局默认 logger通用日志
static std::shared_ptr<spdlog::logger> GetDefaultLogger();
// 获取指定模块的 logger局部 logger支持单独设置级别
// 模块名示例:"net"(网络模块)、"db"(数据库模块)、"ui"(界面模块)
static std::shared_ptr<spdlog::logger> GetModuleLogger(const std::string& module_name);
// 设置全局默认日志级别(对所有未单独设置级别的 logger 生效)
static void SetGlobalLevel(spdlog::level::level_enum level);
// 设置指定模块 logger 的局部级别(优先级高于全局级别)
static void SetModuleLevel(const std::string& module_name, spdlog::level::level_enum level);
// 关闭所有 logger释放资源建议程序退出时调用
static void Shutdown();
private:
// 私有构造函数(单例)
Logger();
// 析构函数
~Logger() = default;
// 单例实例
static Logger& GetInstance();
// 全局线程池(异步日志使用)- 修复thread_pool在spdlog::details命名空间下
std::shared_ptr<spdlog::details::thread_pool> thread_pool_;
// 全局默认 logger
std::shared_ptr<spdlog::logger> default_logger_;
// 新增存储全局日志格式供模块logger复用避免formatter()方法兼容问题)
std::string log_pattern_;
};
// 便捷宏定义(简化调用,可根据需求开启/关闭)
#define LOG_INFO(...) Logger::GetDefaultLogger()->info(__VA_ARGS__)
#define LOG_WARN(...) Logger::GetDefaultLogger()->warn(__VA_ARGS__)
#define LOG_ERROR(...) Logger::GetDefaultLogger()->error(__VA_ARGS__)
#define LOG_DEBUG(...) Logger::GetDefaultLogger()->debug(__VA_ARGS__)
#define LOG_CRITICAL(...) Logger::GetDefaultLogger()->critical(__VA_ARGS__)
#define LOG_TRACE(...) Logger::GetDefaultLogger()->trace(__VA_ARGS__)
// 模块日志宏定义(示例,可自行扩展)
#define LOG_NET_INFO(...) Logger::GetModuleLogger("net")->info(__VA_ARGS__)
#define LOG_DB_WARN(...) Logger::GetModuleLogger("db")->warn(__VA_ARGS__)
#define LOG_UI_ERROR(...) Logger::GetModuleLogger("ui")->error(__VA_ARGS__)

0
python/.gitkeep Normal file
View File

156
src/logger/logger.cpp Normal file
View File

@@ -0,0 +1,156 @@
#include "logger/logger.hpp"
#include <filesystem>
#include <iostream>
#include <spdlog/details/thread_pool.h> // 新增引入thread_pool所在头文件
// 私有构造函数,初始化日志核心配置
Logger::Logger() {
// 1. 创建日志目录(若不存在)
std::filesystem::path log_dir = std::filesystem::path("logs");
if (!std::filesystem::exists(log_dir)) {
try {
std::filesystem::create_directories(log_dir);
} catch (const std::exception& e) {
std::cerr << "Failed to create log directory: " << e.what() << std::endl;
}
}
}
// 单例实例获取(懒汉模式,线程安全)
Logger& Logger::GetInstance() {
static Logger instance;
return instance;
}
// 初始化 logger核心方法
void Logger::Init(const std::string& log_path, size_t max_file_size_mb, size_t max_backup_count, bool is_async) {
auto& instance = GetInstance();
// 1. 配置日志格式统一格式包含时间、模块名、级别、线程ID、日志内容
std::string log_pattern = "[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [thread %t] %v";
// 2. 创建 sink控制台 + 滚动文件)
// 控制台 sink带颜色
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// 滚动文件 sink按大小滚动避免单个文件过大
size_t max_file_size = max_file_size_mb * 1024 * 1024; // 转换为字节
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
log_path, max_file_size, max_backup_count, true // true = 追加模式
);
// 3. 组合 sink同时输出到控制台和文件
spdlog::sinks_init_list sinks = {console_sink, file_sink};
// 4. 初始化异步日志(若开启)
if (is_async) {
// 线程池配置:队列大小 8192线程数 4可根据CPU核心数调整
// 修复1thread_pool在spdlog::details命名空间下
instance.thread_pool_ = std::make_shared<spdlog::details::thread_pool>(8192, 4);
// 创建异步默认 logger
instance.default_logger_ = std::make_shared<spdlog::async_logger>(
"default", sinks.begin(), sinks.end(),
instance.thread_pool_,
spdlog::async_overflow_policy::block // 队列满时阻塞(避免日志丢失,生产环境推荐)
);
} else {
// 同步日志(简单场景使用)
instance.default_logger_ = std::make_shared<spdlog::logger>("default", sinks);
}
// 5. 全局配置(默认级别、格式、刷新策略)
instance.default_logger_->set_pattern(log_pattern);
instance.default_logger_->set_level(spdlog::level::info); // 默认全局级别info
spdlog::set_default_logger(instance.default_logger_);
// 6. 刷新策略(确保日志及时写入,避免崩溃丢失)
spdlog::flush_every(std::chrono::seconds(3)); // 每3秒自动刷新
spdlog::flush_on(spdlog::level::err); // 错误级别日志立即刷新
// 初始化提示(仅控制台输出)
// 修复2sink的log方法只接受log_msg参数改用logger输出
instance.default_logger_->info("Logger initialized successfully!");
}
// 获取全局默认 logger
std::shared_ptr<spdlog::logger> Logger::GetDefaultLogger() {
auto& instance = GetInstance();
if (!instance.default_logger_) {
std::cerr << "Logger not initialized! Call Logger::Init() first." << std::endl;
// 若未初始化,返回一个临时控制台 logger避免崩溃
return spdlog::stdout_color_mt("temp_default");
}
return instance.default_logger_;
}
// 获取指定模块的 logger局部 logger
std::shared_ptr<spdlog::logger> Logger::GetModuleLogger(const std::string& module_name) {
auto& instance = GetInstance();
if (!instance.default_logger_) {
std::cerr << "Logger not initialized! Call Logger::Init() first." << std::endl;
return spdlog::stdout_color_mt(module_name + "_temp");
}
// 若模块 logger 已存在,直接返回;不存在则创建
auto module_logger = spdlog::get(module_name);
if (!module_logger) {
// 模块 logger 复用 控制台+文件 sink与默认 logger 输出一致
// 修复3sinks()返回vector无法直接转为sinks_init_list改用手动添加适配所有spdlog版本
auto& default_sinks = instance.default_logger_->sinks();
std::vector<std::shared_ptr<spdlog::sinks::sink>> module_sinks;
for (auto& sink : default_sinks) {
module_sinks.push_back(sink);
}
// 若开启异步,使用全局线程池;否则同步创建
// 修复4thread_pool在spdlog::details命名空间下
if (instance.thread_pool_) {
module_logger = std::make_shared<spdlog::async_logger>(
module_name, module_sinks.begin(), module_sinks.end(),
instance.thread_pool_,
spdlog::async_overflow_policy::block
);
} else {
module_logger = std::make_shared<spdlog::logger>(module_name, module_sinks.begin(), module_sinks.end());
}
// 模块 logger 继承全局格式,级别可后续单独设置
module_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [thread %t] %v");
module_logger->set_level(instance.default_logger_->level()); // 初始继承全局级别
spdlog::register_logger(module_logger); // 注册到 spdlog 全局管理
}
return module_logger;
}
// 设置全局默认日志级别
void Logger::SetGlobalLevel(spdlog::level::level_enum level) {
auto& instance = GetInstance();
if (instance.default_logger_) {
instance.default_logger_->set_level(level);
// 同步所有未单独设置级别的模块 logger可选根据需求调整
// 修复6spdlog无get_all()方法改用spdlog::get()遍历已注册模块(这里简化实现,适配所有版本)
// 常用模块名可手动添加,避免版本兼容问题
std::vector<std::string> module_names = {"net", "db", "ui"};
for (const auto& name : module_names) {
auto logger = spdlog::get(name);
if (logger && logger->level() == instance.default_logger_->level()) {
logger->set_level(level);
}
}
} else {
std::cerr << "Logger not initialized! Call Logger::Init() first." << std::endl;
}
}
// 设置指定模块 logger 的局部级别
void Logger::SetModuleLevel(const std::string& module_name, spdlog::level::level_enum level) {
auto module_logger = spdlog::get(module_name);
if (module_logger) {
module_logger->set_level(level);
} else {
std::cerr << "Module logger [" << module_name << "] not found! Call GetModuleLogger() first." << std::endl;
}
}
// 关闭所有 logger释放资源
void Logger::Shutdown() {
spdlog::shutdown();
std::cout << "Logger shutdown successfully!" << std::endl;
}

8
src/main.cpp Normal file
View File

@@ -0,0 +1,8 @@
#include "logger/logger.hpp"
int main() {
Logger::Init();
Logger::SetGlobalLevel(spdlog::level::debug);
}

59
tests/test_eigen.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include "krylov/types/common.hpp"
#include <cassert>
#include <cmath>
static bool approx(double a, double b, double eps = 1e-10) {
return std::abs(a - b) < eps;
}
int main() {
using krylov::Matrix;
using krylov::Vector;
using MatrixXd = Matrix;
using VectorXd = Vector;
// 1) 基本矩阵 / 向量运算
MatrixXd A(2, 2);
A << 4, 1,
2, 3;
VectorXd b(2);
b << 1, 2;
VectorXd Ab = A * b;
assert(approx(Ab(0), 4 * 1 + 1 * 2));
assert(approx(Ab(1), 2 * 1 + 3 * 2));
// 2) 行列式 / 逆
assert(approx(A.determinant(), 4 * 3 - 1 * 2));
MatrixXd I = A * A.inverse();
assert(approx(I(0, 0), 1.0));
assert(approx(I(1, 1), 1.0));
assert(approx(I(0, 1), 0.0));
assert(approx(I(1, 0), 0.0));
// 3) 线性方程求解 A x = b
VectorXd x = A.colPivHouseholderQr().solve(b);
VectorXd r = A * x - b;
assert(r.norm() < 1e-10);
// 4) 范数
VectorXd v(3);
v << 3, 0, 4;
assert(approx(v.norm(), 5.0));
assert(approx(v.squaredNorm(), 25.0));
// 5) 稀疏矩阵基础
krylov::SparseMatrix S(3, 3);
S.insert(0, 0) = 1.0;
S.insert(1, 1) = 2.0;
S.insert(2, 2) = 3.0;
S.makeCompressed();
VectorXd y(3);
y << 1, 1, 1;
VectorXd Sy = S * y;
assert(approx(Sy(0), 1.0));
assert(approx(Sy(1), 2.0));
assert(approx(Sy(2), 3.0));
return 0;
}

57
tests/test_logger.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "logger/logger.hpp"
#include <cassert>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
namespace fs = std::filesystem;
static std::string read_file(const fs::path& p) {
std::ifstream in(p);
std::stringstream ss;
ss << in.rdbuf();
return ss.str();
}
int main() {
const fs::path log_path = "logs/test_logger.log";
if (fs::exists(log_path)) fs::remove(log_path);
// 同步模式便于立即断言文件内容
Logger::Init(log_path.string(), 1, 2, /*is_async=*/false);
// 1) 默认 logger 可用
auto def = Logger::GetDefaultLogger();
assert(def != nullptr);
// 2) 全局级别设置生效
Logger::SetGlobalLevel(spdlog::level::debug);
assert(def->level() == spdlog::level::debug);
// 3) 模块 logger 创建 + 复用
auto net1 = Logger::GetModuleLogger("net");
auto net2 = Logger::GetModuleLogger("net");
assert(net1 != nullptr);
assert(net1.get() == net2.get());
// 4) 模块级别独立设置
Logger::SetModuleLevel("net", spdlog::level::warn);
assert(net1->level() == spdlog::level::warn);
// 5) 写日志并落盘
LOG_INFO("hello {}", "logger");
LOG_DEBUG("debug value = {}", 123);
net1->error("net module error {}", 7);
def->flush();
net1->flush();
assert(fs::exists(log_path));
std::string content = read_file(log_path);
assert(content.find("hello logger") != std::string::npos);
assert(content.find("debug value = 123") != std::string::npos);
assert(content.find("net module error 7") != std::string::npos);
Logger::Shutdown();
return 0;
}