// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // common.hpp // 共通定義、例外、設定 // // このファイルでは、MKL代数ライブラリ全体で使用される共通の定義、 // 例外クラス、グローバル設定、およびユーティリティ関数を提供します。 // // 主な機能: // - 名前空間の定義 // - バージョン情報 // - MKL検出と設定 // - 例外クラス階層 // - 共通ユーティリティ関数 // - コンパイル時設定 // - スレッド管理 // - 状態管理関連の例外と設定 #ifndef SANGI_COMMON_HPP #define SANGI_COMMON_HPP // コンパイラ互換マクロ #ifdef _MSC_VER #define SANGI_FORCEINLINE __forceinline #else #define SANGI_FORCEINLINE __attribute__((always_inline)) inline #endif // ============================================================================== // DLL エクスポート/インポート マクロ // ============================================================================== // SANGI_SHARED_LIB: DLL ビルド時に定義 (CMake が設定) // SANGI_BUILDING_CORE: sangi_core ライブラリ自身をビルド中に定義 // // 使い方: // class SANGI_API Int { ... }; → DLL エクスポート/インポート対象 // SANGI_API int someFunction(); → 関数のエクスポート // ヘッダオンリーの template は不要 → テンプレートは SANGI_API 不要 // #ifdef SANGI_SHARED_LIB #ifdef _MSC_VER #ifdef SANGI_BUILDING_CORE #define SANGI_API __declspec(dllexport) #else #define SANGI_API __declspec(dllimport) #endif #else #define SANGI_API __attribute__((visibility("default"))) #endif #else #define SANGI_API #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ライブラリの名前空間 namespace sangi { // バージョン情報構造体 struct Version { static constexpr int major = VERSION_MAJOR; static constexpr int minor = VERSION_MINOR; static constexpr int patch = VERSION_PATCH; // バージョン文字列の取得 static constexpr std::string_view string() { return "2.0.0"; // 実際には動的に生成すべきだが、constexprの制約により固定値 } }; // プラットフォーム検出 #if defined(_WIN32) || defined(_WIN64) #define SANGI_PLATFORM_WINDOWS #elif defined(__APPLE__) #define SANGI_PLATFORM_MACOS #elif defined(__linux__) #define SANGI_PLATFORM_LINUX #else #define SANGI_PLATFORM_UNKNOWN #endif // コンパイラ検出 #if defined(_MSC_VER) #define SANGI_COMPILER_MSVC #elif defined(__clang__) #define SANGI_COMPILER_CLANG #elif defined(__GNUC__) #define SANGI_COMPILER_GCC #else #define SANGI_COMPILER_UNKNOWN #endif // C++バージョン検出 #if defined(_MSC_VER) // Visual Studio の場合は /std:c++XX オプションを検出 #if defined(_MSVC_LANG) #if _MSVC_LANG >= 202302L #define SANGI_CPP23 #define SANGI_CPP20 #elif _MSVC_LANG >= 202002L #define SANGI_CPP20 #else // C++20未満でもコンパイルできるようにエラーは無効化 // #error "C++20 or later is required" #endif #else // /std:c++XX オプションが指定されていない場合 // #error "Please specify /std:c++latest or /std:c++20 or later" #endif #else // 他のコンパイラ #if __cplusplus >= 202302L #define SANGI_CPP23 #define SANGI_CPP20 #elif __cplusplus >= 202002L #define SANGI_CPP20 #else // C++20未満でもコンパイルできるようにエラーは無効化 // #error "C++20 or later is required" #endif #endif // MKL検出 #if defined(SANGI_USE_MKL) #if defined(SANGI_PLATFORM_WINDOWS) #include #else #include #endif #define SANGI_HAS_MKL 1 #else #define SANGI_HAS_MKL 0 #endif // OpenBLAS検出(MKLのフォールバック) #if !defined(SANGI_HAS_MKL) && defined(SANGI_USE_OPENBLAS) #include #define SANGI_HAS_OPENBLAS 1 #else #define SANGI_HAS_OPENBLAS 0 #endif // 利用可能なSIMD命令セットの検出 constexpr SimdInstructionSet detect_simd_level() { #if defined(__AVX512F__) return SimdInstructionSet::AVX512; #elif defined(__AVX2__) return SimdInstructionSet::AVX2; #elif defined(__AVX__) return SimdInstructionSet::AVX; #elif defined(__SSE4_2__) return SimdInstructionSet::SSE4_2; #elif defined(__SSE4_1__) return SimdInstructionSet::SSE4_1; #elif defined(__SSSE3__) return SimdInstructionSet::SSSE3; #elif defined(__SSE3__) return SimdInstructionSet::SSE3; #elif defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) return SimdInstructionSet::SSE2; #elif defined(__ARM_NEON) || defined(__ARM_NEON__) return SimdInstructionSet::NEON; #else return SimdInstructionSet::None; #endif } // 利用可能なSIMD命令セット constexpr SimdInstructionSet available_simd_level = detect_simd_level(); // 数学定数 namespace constants { template inline constexpr T pi = static_cast(3.14159265358979323846); template inline constexpr T e = static_cast(2.71828182845904523536); template inline constexpr T sqrt2 = static_cast(1.41421356237309504880); template inline constexpr T log2e = static_cast(1.44269504088896340736); template inline constexpr T log10e = static_cast(0.43429448190325182765); } // 例外クラス階層 // MKL代数ライブラリの基本例外クラス class MklAlgebraException : public std::exception { public: explicit MklAlgebraException(const std::string& message) : message_(message) {} explicit MklAlgebraException(std::string&& message) : message_(std::move(message)) {} const char* what() const noexcept override { return message_.c_str(); } private: std::string message_; }; // 数学的エラー(無効な操作、ドメインエラーなど) class MathError : public MklAlgebraException { public: using MklAlgebraException::MklAlgebraException; }; // 次元エラー(行列やベクトルの次元の不一致) class DimensionError : public MklAlgebraException { public: using MklAlgebraException::MklAlgebraException; }; // インデックスエラー(範囲外のインデックスアクセス) class IndexError : public MklAlgebraException { public: using MklAlgebraException::MklAlgebraException; }; // 線形代数エラー(特異行列など) class LinearAlgebraError : public MklAlgebraException { public: using MklAlgebraException::MklAlgebraException; }; // 収束エラー(数値アルゴリズムが収束しない) class ConvergenceError : public MklAlgebraException { public: using MklAlgebraException::MklAlgebraException; }; // MKLエラー(MKL関数が失敗) class MklError : public MklAlgebraException { public: explicit MklError(const std::string& message, int error_code) : MklAlgebraException(message + " (error code: " + std::to_string(error_code) + ")"), error_code_(error_code) { } int error_code() const noexcept { return error_code_; } private: int error_code_; }; // 数値状態関連のエラー class NumericStateError : public MathError { public: explicit NumericStateError(const std::string& message) : MathError(message), state_(NumericState::Normal), error_(NumericError::None) { } explicit NumericStateError(const std::string& message, NumericState state, NumericError error = NumericError::None) : MathError(message), state_(state), error_(error) { } NumericState getState() const noexcept { return state_; } NumericError getError() const noexcept { return error_; } private: NumericState state_; NumericError error_; }; // NaNエラー class NaNError : public NumericStateError { public: explicit NaNError(const std::string& message = "Operation resulted in NaN") : NumericStateError(message, NumericState::NaN, NumericError::ExplicitNaN) { } }; // 無限大エラー class InfinityError : public NumericStateError { public: explicit InfinityError(const std::string& message = "Operation resulted in infinity", bool is_positive = true) : NumericStateError(message, is_positive ? NumericState::PositiveInfinity : NumericState::NegativeInfinity, NumericError::None), is_positive_(is_positive) { } bool isPositive() const noexcept { return is_positive_; } private: bool is_positive_; }; // オーバーフローエラー class OverflowError : public NumericStateError { public: explicit OverflowError(const std::string& message = "Numeric overflow occurred") : NumericStateError(message, NumericState::Overflow, NumericError::OutOfRangeInput) { } }; // アンダーフローエラー class UnderflowError : public NumericStateError { public: explicit UnderflowError(const std::string& message = "Numeric underflow occurred") : NumericStateError(message, NumericState::Underflow, NumericError::OutOfRangeInput) { } }; // ゼロ除算エラー class DivideByZeroError : public NumericStateError { public: explicit DivideByZeroError(const std::string& message = "Division by zero") : NumericStateError(message, NumericState::NaN, NumericError::DivideByZero) { } }; // 発散エラー class DivergenceStateError : public NumericStateError { public: explicit DivergenceStateError(const std::string& message = "Algorithm diverged", DivergenceDetail detail = DivergenceDetail::Diverging) : NumericStateError(message, NumericState::Divergent, NumericError::DivergenceError), detail_(detail) { } DivergenceDetail getDetail() const noexcept { return detail_; } private: DivergenceDetail detail_; }; // 状態エラー処理設定 struct StateErrorOptions { // 各エラータイプに対する処理モード StateErrorMode nan_error_mode = StateErrorMode::ReturnSpecial; // NaNに関するエラー StateErrorMode infinity_error_mode = StateErrorMode::ReturnSpecial; // 無限大に関するエラー StateErrorMode overflow_error_mode = StateErrorMode::ReturnSpecial; // オーバーフローに関するエラー StateErrorMode underflow_error_mode = StateErrorMode::ReturnSpecial; // アンダーフローに関するエラー StateErrorMode divide_by_zero_error_mode = StateErrorMode::ReturnSpecial; // ゼロ除算に関するエラー StateErrorMode divergence_error_mode = StateErrorMode::Exception; // 発散に関するエラー // デフォルト設定 static StateErrorOptions defaults() { return {}; } // 例外のみのモード static StateErrorOptions exceptions_only() { StateErrorOptions options; options.nan_error_mode = StateErrorMode::Exception; options.infinity_error_mode = StateErrorMode::Exception; options.overflow_error_mode = StateErrorMode::Exception; options.underflow_error_mode = StateErrorMode::Exception; options.divide_by_zero_error_mode = StateErrorMode::Exception; options.divergence_error_mode = StateErrorMode::Exception; return options; } // 特殊値のみのモード static StateErrorOptions special_values_only() { StateErrorOptions options; options.nan_error_mode = StateErrorMode::ReturnSpecial; options.infinity_error_mode = StateErrorMode::ReturnSpecial; options.overflow_error_mode = StateErrorMode::ReturnSpecial; options.underflow_error_mode = StateErrorMode::ReturnSpecial; options.divide_by_zero_error_mode = StateErrorMode::ReturnSpecial; options.divergence_error_mode = StateErrorMode::ReturnSpecial; return options; } // サイレントモード static StateErrorOptions silent() { StateErrorOptions options; options.nan_error_mode = StateErrorMode::Silent; options.infinity_error_mode = StateErrorMode::Silent; options.overflow_error_mode = StateErrorMode::Silent; options.underflow_error_mode = StateErrorMode::Silent; options.divide_by_zero_error_mode = StateErrorMode::Silent; options.divergence_error_mode = StateErrorMode::Silent; return options; } // NumericErrorに基づくモード取得 StateErrorMode getModeForError(NumericError error) const { switch (error) { case NumericError::DivideByZero: return divide_by_zero_error_mode; case NumericError::NaNPropagation: case NumericError::ExplicitNaN: return nan_error_mode; case NumericError::OutOfRangeInput: case NumericError::ConversionError: case NumericError::IntegerConversionError: return overflow_error_mode; case NumericError::DivergenceError: return divergence_error_mode; default: return StateErrorMode::ReturnSpecial; } } // NumericStateに基づくモード取得 StateErrorMode getModeForState(NumericState state) const { if (NumericStateTraits::isNaN(state)) { return nan_error_mode; } if (NumericStateTraits::isInfinite(state)) { return infinity_error_mode; } if (state == NumericState::Overflow) { return overflow_error_mode; } if (state == NumericState::Underflow) { return underflow_error_mode; } if (NumericStateTraits::isDivergent(state)) { return divergence_error_mode; } return StateErrorMode::ReturnSpecial; } }; // グローバルな状態エラー処理設定(スレッドセーフではない) inline StateErrorOptions& global_state_error_options() { static StateErrorOptions options = StateErrorOptions::defaults(); return options; } // StateErrorOptionsを一時的に変更し、スコープ終了時に元に戻すためのガードクラス class StateErrorOptionsGuard { private: StateErrorOptions& options_; StateErrorMode original_nan_mode_; StateErrorMode original_infinity_mode_; StateErrorMode original_overflow_mode_; StateErrorMode original_underflow_mode_; StateErrorMode original_divide_by_zero_mode_; StateErrorMode original_divergence_mode_; public: explicit StateErrorOptionsGuard(StateErrorOptions& options) : options_(options), original_nan_mode_(options.nan_error_mode), original_infinity_mode_(options.infinity_error_mode), original_overflow_mode_(options.overflow_error_mode), original_underflow_mode_(options.underflow_error_mode), original_divide_by_zero_mode_(options.divide_by_zero_error_mode), original_divergence_mode_(options.divergence_error_mode) { } ~StateErrorOptionsGuard() { // デストラクタで設定を元に戻す options_.nan_error_mode = original_nan_mode_; options_.infinity_error_mode = original_infinity_mode_; options_.overflow_error_mode = original_overflow_mode_; options_.underflow_error_mode = original_underflow_mode_; options_.divide_by_zero_error_mode = original_divide_by_zero_mode_; options_.divergence_error_mode = original_divergence_mode_; } // コピー禁止 StateErrorOptionsGuard(const StateErrorOptionsGuard&) = delete; StateErrorOptionsGuard& operator=(const StateErrorOptionsGuard&) = delete; }; // 結果型(エラー処理のためのユーティリティ) template class Result { public: // 成功結果の作成 static Result success(T value) { return Result(std::move(value), std::nullopt); } // エラー結果の作成 static Result error(std::string error_message) { return Result(std::nullopt, std::move(error_message)); } // 結果が成功かどうか bool is_success() const { return value_.has_value(); } // 結果がエラーかどうか bool is_error() const { return error_message_.has_value(); } // 値の取得(エラーの場合は例外を投げる) const T& value() const { if (is_error()) { throw MklAlgebraException(error_message_.value()); } return value_.value(); } // 値の取得(エラーの場合は例外を投げる) T& value() { if (is_error()) { throw MklAlgebraException(error_message_.value()); } return value_.value(); } // エラーメッセージの取得(成功の場合は空文字列) std::string error_message() const { return error_message_.value_or(""); } private: Result(std::optional value, std::optional error_message) : value_(std::move(value)), error_message_(std::move(error_message)) { } std::optional value_; std::optional error_message_; }; // ユーティリティ関数 // 次元チェック(不一致の場合は例外を投げる) inline void check_dimensions(std::size_t expected, std::size_t actual, const std::string& message) { if (expected != actual) { throw DimensionError(message + ": expected " + std::to_string(expected) + ", got " + std::to_string(actual)); } } // インデックスチェック(範囲外の場合は例外を投げる) inline void check_index(std::size_t index, std::size_t size, const std::string& message) { if (index >= size) { throw IndexError(message + ": index " + std::to_string(index) + " out of range for size " + std::to_string(size)); } } // 正方行列チェック(非正方の場合は例外を投げる) inline void check_square(std::size_t rows, std::size_t cols, const std::string& message) { if (rows != cols) { throw DimensionError(message + ": matrix is not square (" + std::to_string(rows) + "x" + std::to_string(cols) + ")"); } } // 数値状態チェック関数 template void check_numeric_state(T value, const std::string& operation = "Operation") { const auto& options = global_state_error_options(); // NaNチェック if (std::isnan(value)) { switch (options.nan_error_mode) { case StateErrorMode::Exception: throw NaNError(operation + " resulted in NaN"); case StateErrorMode::ReturnSpecial: // 特殊値を返す場合は何もしない(valueがすでにNaN) break; case StateErrorMode::Silent: // サイレントモードでは何もしない break; } } // 無限大チェック if (std::isinf(value)) { switch (options.infinity_error_mode) { case StateErrorMode::Exception: throw InfinityError(operation + " resulted in infinity"); case StateErrorMode::ReturnSpecial: // 特殊値を返す場合は何もしない(valueがすでに無限大) break; case StateErrorMode::Silent: // サイレントモードでは何もしない break; } } // オーバーフローとアンダーフローのチェックは // 浮動小数点演算では難しいため、ここでは実装しない } // 除算前のゼロチェック inline void check_divide_by_zero(double divisor, const std::string& message = "Division") { if (divisor == 0.0) { const auto& options = global_state_error_options(); switch (options.divide_by_zero_error_mode) { case StateErrorMode::Exception: throw DivideByZeroError(message + " by zero"); case StateErrorMode::ReturnSpecial: case StateErrorMode::Silent: // これらのモードでは何もしない(呼び出し側で無限大か特殊値を生成) break; } } } // 数値比較関数(浮動小数点比較) template constexpr bool approximately_equal(T a, T b, T epsilon = std::numeric_limits::epsilon() * T(100)) { if (a == b) return true; auto safe_abs = [](const T& x) -> T { return (x < T(0)) ? -x : x; }; const T diff = safe_abs(a - b); const T norm = std::min((safe_abs(a) + safe_abs(b)), std::numeric_limits::max()); return diff < std::max(std::numeric_limits::min(), epsilon * norm); } // 数値状態文字列変換 inline std::string numeric_state_to_string(NumericState state) { return NumericStateTraits::toString(state); } // 発散詳細文字列変換 inline std::string divergence_detail_to_string(DivergenceDetail detail) { return NumericStateTraits::toString(detail); } // エラー原因文字列変換 inline std::string numeric_error_to_string(NumericError error) { return NumericStateTraits::toString(error); } // 状態とエラーを同時に文字列化 inline std::string state_and_error_to_string(NumericState state, NumericError error) { if (error == NumericError::None) { return NumericStateTraits::toString(state); } else { return NumericStateTraits::toString(state) + " (" + NumericStateTraits::toString(error) + ")"; } } // スレッド管理(MKLのスレッド数設定など) #if SANGI_HAS_MKL class ThreadManager { public: // 利用可能なスレッド数の取得 static int get_max_threads() { return mkl_get_max_threads(); } // 現在のスレッド数の取得 static int get_num_threads() { return mkl_get_max_threads(); } // スレッド数の設定 static void set_num_threads(int num_threads) { mkl_set_num_threads(num_threads); } // スレッド数の一時的な変更(スコープが終了すると元に戻る) class ScopedThreads { public: explicit ScopedThreads(int num_threads) : previous_threads_(mkl_get_max_threads()) { mkl_set_num_threads(num_threads); } ~ScopedThreads() { mkl_set_num_threads(previous_threads_); } // コピー不可 ScopedThreads(const ScopedThreads&) = delete; ScopedThreads& operator=(const ScopedThreads&) = delete; private: const int previous_threads_; }; }; #else class ThreadManager { public: // MKLが利用できない場合のスタブ実装 static int get_max_threads() { return std::thread::hardware_concurrency(); } static int get_num_threads() { return 1; } static void set_num_threads(int /* num_threads */) { // スタブ実装では何もしない } class ScopedThreads { public: explicit ScopedThreads(int /* num_threads */) {} // コピー不可 ScopedThreads(const ScopedThreads&) = delete; ScopedThreads& operator=(const ScopedThreads&) = delete; }; }; #endif // メモリアラインメント関連 namespace memory { // 必要なアラインメントの計算 constexpr std::size_t simd_alignment() { switch (available_simd_level) { case SimdInstructionSet::AVX512: return 64; case SimdInstructionSet::AVX2: case SimdInstructionSet::AVX: return 32; case SimdInstructionSet::SSE4_2: case SimdInstructionSet::SSE4_1: case SimdInstructionSet::SSSE3: case SimdInstructionSet::SSE3: case SimdInstructionSet::SSE2: case SimdInstructionSet::NEON: return 16; default: return 8; } } // アラインメントの調整 inline std::size_t align_to(std::size_t size, std::size_t alignment) { return (size + alignment - 1) & ~(alignment - 1); } // アラインメント済みメモリの確保 template T* aligned_alloc(std::size_t size, std::size_t alignment = simd_alignment()) { // C++17のstd::aligned_allocまたは相当する関数を使用 #if defined(SANGI_COMPILER_MSVC) return static_cast(_aligned_malloc(size * sizeof(T), alignment)); #elif defined(_POSIX_VERSION) void* ptr = nullptr; if (posix_memalign(&ptr, alignment, size * sizeof(T)) != 0) { return nullptr; } return static_cast(ptr); #else // 一般的なマシンで使えるアライメント確保のフォールバック実装 void* ptr = std::malloc(size * sizeof(T) + alignment); if (!ptr) return nullptr; std::size_t offset = alignment - (reinterpret_cast(ptr) % alignment); T* aligned = reinterpret_cast(reinterpret_cast(ptr) + offset); // ポインタを前に戻すために必要な情報を保存 *(reinterpret_cast(aligned) - 1) = ptr; return aligned; #endif } // アラインメント済みメモリの解放 template void aligned_free(T* ptr) { #if defined(SANGI_COMPILER_MSVC) _aligned_free(ptr); #elif defined(_POSIX_VERSION) free(ptr); #else // 一般的なマシンで使えるアライメント解放のフォールバック実装 if (!ptr) return; // 元のポインタを取得して解放 void* original = *(reinterpret_cast(ptr) - 1); std::free(original); #endif } } // 一般的な数値状態チェック関数(更新版) inline void check_numeric_state(NumericState state, NumericError error, const std::string& message) { const auto& options = global_state_error_options(); // エラーに基づくチェック if (error != NumericError::None) { switch (error) { case NumericError::DivideByZero: if (options.divide_by_zero_error_mode == StateErrorMode::Exception) { throw DivideByZeroError(message); } break; case NumericError::NaNPropagation: case NumericError::ExplicitNaN: case NumericError::InvalidBitPattern: if (options.nan_error_mode == StateErrorMode::Exception) { throw NaNError(message); } break; case NumericError::OutOfRangeInput: case NumericError::ConversionError: case NumericError::IntegerConversionError: if (options.overflow_error_mode == StateErrorMode::Exception) { throw OverflowError(message); } break; case NumericError::DivergenceError: if (options.divergence_error_mode == StateErrorMode::Exception) { throw DivergenceStateError(message); } break; default: // その他のエラーは特に処理しない break; } } // 状態に基づくチェック if (NumericStateTraits::isNaN(state)) { if (options.nan_error_mode == StateErrorMode::Exception) { throw NaNError(message); } return; } // 無限大チェック if (NumericStateTraits::isInfinite(state)) { if (options.infinity_error_mode == StateErrorMode::Exception) { throw InfinityError(message, state == NumericState::PositiveInfinity); } return; } // オーバーフローチェック if (state == NumericState::Overflow) { if (options.overflow_error_mode == StateErrorMode::Exception) { throw OverflowError(message); } return; } // アンダーフローチェック if (state == NumericState::Underflow) { if (options.underflow_error_mode == StateErrorMode::Exception) { throw UnderflowError(message); } return; } // 発散チェック if (NumericStateTraits::isDivergent(state)) { if (options.divergence_error_mode == StateErrorMode::Exception) { throw DivergenceStateError(message); } return; } } } // namespace sangi #endif // SANGI_COMMON_HPP