From 173ad1cf2f73cc14bd8e7a453a7432f71d024c8c Mon Sep 17 00:00:00 2001 From: M4yGem1ni <3164864582@qq.com> Date: Thu, 9 Apr 2026 19:38:56 +0800 Subject: [PATCH] =?UTF-8?q?init:=20=E5=88=9D=E5=A7=8B=E5=8C=96krylov?= =?UTF-8?q?=E5=A4=8D=E7=8E=B0=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 58 ++++++++++++ CMakeLists.txt | 78 ++++++++++++++++ README.md | 0 data/.gitkeep | 0 include/krylov/arnoldi.hpp | 1 + include/krylov/gmres.hpp | 1 + include/krylov/types/common.hpp | 31 +++++++ include/logger/logger.hpp | 69 ++++++++++++++ python/.gitkeep | 0 src/logger/logger.cpp | 156 ++++++++++++++++++++++++++++++++ src/main.cpp | 8 ++ tests/test_eigen.cpp | 59 ++++++++++++ tests/test_logger.cpp | 57 ++++++++++++ 13 files changed, 518 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 data/.gitkeep create mode 100644 include/krylov/arnoldi.hpp create mode 100644 include/krylov/gmres.hpp create mode 100644 include/krylov/types/common.hpp create mode 100644 include/logger/logger.hpp create mode 100644 python/.gitkeep create mode 100644 src/logger/logger.cpp create mode 100644 src/main.cpp create mode 100644 tests/test_eigen.cpp create mode 100644 tests/test_logger.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fd0864 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9be1e35 --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/include/krylov/arnoldi.hpp b/include/krylov/arnoldi.hpp new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/include/krylov/arnoldi.hpp @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/include/krylov/gmres.hpp b/include/krylov/gmres.hpp new file mode 100644 index 0000000..7b9637e --- /dev/null +++ b/include/krylov/gmres.hpp @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/include/krylov/types/common.hpp b/include/krylov/types/common.hpp new file mode 100644 index 0000000..7849e18 --- /dev/null +++ b/include/krylov/types/common.hpp @@ -0,0 +1,31 @@ +#pragma once + +// Krylov 项目公共数值类型别名。 +// +// 约定:算法代码只使用本文件中的别名,不直接写裸 Eigen 类型。 +// 后续若要切换精度 (float/double)、索引宽度或矩阵存储布局, +// 只需改动此文件,无需触碰算法实现。 +// +// 规模变大后可拆分为: +// - krylov/types/dense.hpp +// - krylov/types/sparse.hpp +// - krylov/types/traits.hpp + +#include +#include + +namespace krylov { + +// 标量与索引 +using Scalar = double; +using Index = Eigen::Index; + +// 稠密 +using Vector = Eigen::Matrix; +using Matrix = Eigen::Matrix; + +// 稀疏(行主序,方便按行遍历 SpMV) +using SparseMatrix = Eigen::SparseMatrix; +using Triplet = Eigen::Triplet; + +} // namespace krylov diff --git a/include/logger/logger.hpp b/include/logger/logger.hpp new file mode 100644 index 0000000..f01f807 --- /dev/null +++ b/include/logger/logger.hpp @@ -0,0 +1,69 @@ +#pragma once +#include +#include +#include +#include +#include // 新增:引入thread_pool所在头文件 +#include +#include +#include // 新增:用于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 GetDefaultLogger(); + + // 获取指定模块的 logger(局部 logger,支持单独设置级别) + // 模块名示例:"net"(网络模块)、"db"(数据库模块)、"ui"(界面模块) + static std::shared_ptr 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 thread_pool_; + // 全局默认 logger + std::shared_ptr 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__) diff --git a/python/.gitkeep b/python/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp new file mode 100644 index 0000000..3598de2 --- /dev/null +++ b/src/logger/logger.cpp @@ -0,0 +1,156 @@ +#include "logger/logger.hpp" +#include +#include +#include // 新增:引入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(); + // 滚动文件 sink(按大小滚动,避免单个文件过大) + size_t max_file_size = max_file_size_mb * 1024 * 1024; // 转换为字节 + auto file_sink = std::make_shared( + 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核心数调整) + // 修复1:thread_pool在spdlog::details命名空间下 + instance.thread_pool_ = std::make_shared(8192, 4); + // 创建异步默认 logger + instance.default_logger_ = std::make_shared( + "default", sinks.begin(), sinks.end(), + instance.thread_pool_, + spdlog::async_overflow_policy::block // 队列满时阻塞(避免日志丢失,生产环境推荐) + ); + } else { + // 同步日志(简单场景使用) + instance.default_logger_ = std::make_shared("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); // 错误级别日志立即刷新 + + // 初始化提示(仅控制台输出) + // 修复2:sink的log方法只接受log_msg参数,改用logger输出 + instance.default_logger_->info("Logger initialized successfully!"); +} + +// 获取全局默认 logger +std::shared_ptr 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 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 输出一致 + // 修复3:sinks()返回vector,无法直接转为sinks_init_list,改用手动添加(适配所有spdlog版本) + auto& default_sinks = instance.default_logger_->sinks(); + std::vector> module_sinks; + for (auto& sink : default_sinks) { + module_sinks.push_back(sink); + } + // 若开启异步,使用全局线程池;否则同步创建 + // 修复4:thread_pool在spdlog::details命名空间下 + if (instance.thread_pool_) { + module_logger = std::make_shared( + module_name, module_sinks.begin(), module_sinks.end(), + instance.thread_pool_, + spdlog::async_overflow_policy::block + ); + } else { + module_logger = std::make_shared(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(可选,根据需求调整) + // 修复6:spdlog无get_all()方法,改用spdlog::get()遍历已注册模块(这里简化实现,适配所有版本) + // 常用模块名可手动添加,避免版本兼容问题 + std::vector 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; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..5500f3d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,8 @@ +#include "logger/logger.hpp" + +int main() { + Logger::Init(); + Logger::SetGlobalLevel(spdlog::level::debug); + + +} \ No newline at end of file diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp new file mode 100644 index 0000000..b15a2c6 --- /dev/null +++ b/tests/test_eigen.cpp @@ -0,0 +1,59 @@ +#include "krylov/types/common.hpp" +#include +#include + +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; +} diff --git a/tests/test_logger.cpp b/tests/test_logger.cpp new file mode 100644 index 0000000..b53e723 --- /dev/null +++ b/tests/test_logger.cpp @@ -0,0 +1,57 @@ +#include "logger/logger.hpp" +#include +#include +#include +#include +#include + +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; +}