rshogi-core 0.1.8

A high-performance shogi engine core library with NNUE evaluation
Documentation
//! NNUE定数定義
//!
//! YaneuraOu の HalfKP 256x2-32-32 アーキテクチャに基づき、
//! ネットワーク構造とスケーリングに関する定数をまとめる。

/// 評価関数ファイルのバージョン(YaneuraOu互換)
pub const NNUE_VERSION: u32 = 0x7AF32F16;

/// アーキテクチャ文字列の最大長(破損ファイル/DoS対策)
pub const MAX_ARCH_LEN: usize = 4096;

/// 評価値のスケーリング(水匠5用: 24)
///
/// FV_SCALEは評価関数の訓練時に決まるパラメータ。
/// 同じファイル形式でも評価関数によって異なる場合がある。
/// 例: YaneuraOuのデフォルトは16だが、水匠5は24を使用。
pub const FV_SCALE: i32 = 24;

/// 評価値のスケーリング(デフォルト: 16)
///
/// nnue-pytorchでハードコードされている値(kBiasScale = 600 * 16 = 9600)。
/// YaneuraOuのデフォルト値でもある。
/// bullet-shogiで学習したモデル(scale=600)もこの値で動作する。
pub const FV_SCALE_HALFKA: i32 = 16;

/// 重みのスケーリングビット数
pub const WEIGHT_SCALE_BITS: u32 = 6;

/// SCReLU のデフォルト QA 値
///
/// FT SCReLU 出力は QA に依存せず 0〜127 に正規化されるため、
/// L1/L2 の積和スケールは常に `SCRELU_DEFAULT_QA × QB` になる。
/// QA > 127 の場合は bias スケールを調整する必要がある。
pub const SCRELU_DEFAULT_QA: i32 = 127;

/// キャッシュラインサイズ(バイト)
pub const CACHE_LINE_SIZE: usize = 64;

/// SIMD幅(バイト)
#[cfg(target_feature = "avx2")]
pub const SIMD_WIDTH: usize = 32;

#[cfg(all(target_feature = "sse2", not(target_feature = "avx2")))]
pub const SIMD_WIDTH: usize = 16;

#[cfg(not(any(target_feature = "avx2", target_feature = "sse2")))]
pub const SIMD_WIDTH: usize = 8;

/// 変換後の次元数(片方の視点)
pub const TRANSFORMED_FEATURE_DIMENSIONS: usize = 256;

/// リフレッシュトリガーの数(YO kRefreshTriggers.size() 相当)
/// HalfKP の場合は FriendKingMoved のみで 1
///
/// 注意: この値を変更する場合、以下の箇所も更新が必要:
/// - `Accumulator` の `accumulation` 配列サイズ
/// - `FeatureTransformer` の `refresh_accumulator` / `update_accumulator` / `transform`
/// - `HalfKPFeatureSet::REFRESH_TRIGGERS`
pub const NUM_REFRESH_TRIGGERS: usize = 1;

/// HalfKP特徴量の次元数
/// 81(玉の位置)× FE_END(BonaPiece数)
pub const HALFKP_DIMENSIONS: usize = 81 * super::bona_piece::FE_END;

// =============================================================================
// HalfKA_hm^ アーキテクチャ用定数
// =============================================================================

/// HalfKA_hm^のバージョン(nnue-pytorch互換)
pub const NNUE_VERSION_HALFKA: u32 = 0x7AF32F20;

/// キングバケット数(Half-Mirror: 9段 × 5筋)
pub const KING_BUCKETS: usize = 45;

/// 駒入力数(DISTINGUISH_GOLDS有効時のe_king = 1629)
pub const PIECE_INPUTS_HALFKA: usize = 1629;

/// HalfKA_hm^のベース入力数(キングバケット × 駒入力)
pub const BASE_INPUTS_HALFKA: usize = KING_BUCKETS * PIECE_INPUTS_HALFKA; // 73,305

/// HalfKA_hm^の総入力次元数
///
/// nnue-pytorch標準のcoalesce済みモデル専用。
/// Factorizationの重みはBase側に畳み込み済みのため、推論時はBaseのみで計算する。
/// これにより特徴量数が半減(80→40)し、NPSが約20%向上する。
///
/// 非coalesceモデル(74,934次元)はサポートしない。
/// nnue-pytorch serialize.py でエクスポートすると自動的にcoalesceされる。
pub const HALFKA_HM_DIMENSIONS: usize = BASE_INPUTS_HALFKA; // 73,305

/// HalfKA_hm^のFactorization込み入力次元数(未coalesce)
///
/// 訓練時のみ使用。推論用モデルは serialize.py で自動的に coalesce される。
/// この定数は互換性エラー検出のために定義。
pub const HALFKA_HM_DIMENSIONS_FACTORIZED: usize = BASE_INPUTS_HALFKA + PIECE_INPUTS_HALFKA; // 74,934

// =============================================================================
// HalfKA(非ミラー)アーキテクチャ用定数(Hisui 仕様)
// =============================================================================

/// HalfKA(非ミラー)の入力平面数
///
/// Hisui の学習設定: 1548 + 81 * 2 = 1710
pub const HALFKA_PLANES: usize = 1548 + 81 * 2;

/// HalfKA(非ミラー)の総入力次元数
///
/// 81(玉位置)× 1710(入力平面)
pub const HALFKA_DIMENSIONS: usize = HALFKA_PLANES * 81; // 138,510

/// 隠れ層1の次元数(YaneuraOu classic)
pub const HIDDEN1_DIMENSIONS: usize = 32;

/// 隠れ層2の次元数(YaneuraOu classic)
pub const HIDDEN2_DIMENSIONS: usize = 32;

/// 出力次元数
pub const OUTPUT_DIMENSIONS: usize = 1;

// =============================================================================
// nnue-pytorch LayerStacks アーキテクチャ用定数
// =============================================================================

/// nnue-pytorch の Feature Transformer 出力次元数(片方の視点)
pub const NNUE_PYTORCH_L1: usize = 1536;

/// nnue-pytorch の L2 次元数
pub const NNUE_PYTORCH_L2: usize = 15;

/// nnue-pytorch の L3 次元数
pub const NNUE_PYTORCH_L3: usize = 32;

/// LayerStacks のバケット数
pub const NUM_LAYER_STACK_BUCKETS: usize = 9;

/// LayerStacks L1層の出力次元数(L2 + 1 = 16)
pub const LAYER_STACK_L1_OUT: usize = NNUE_PYTORCH_L2 + 1; // 16

/// LayerStacks L2層の入力次元数(L2 * 2 = 30)
pub const LAYER_STACK_L2_IN: usize = NNUE_PYTORCH_L2 * 2; // 30

/// nnue-pytorch の評価値スケーリング
pub const NNUE_PYTORCH_NNUE2SCORE: i32 = 600;

/// nnue-pytorch の隠れ層重みスケール
pub const NNUE_PYTORCH_WEIGHT_SCALE_HIDDEN: i32 = 64;

/// nnue-pytorch の出力層重みスケール
pub const NNUE_PYTORCH_WEIGHT_SCALE_OUT: i32 = 16;

/// nnue-pytorch の量子化単位
pub const NNUE_PYTORCH_QUANTIZED_ONE: i32 = 127;

// =============================================================================
// SCReLU (Squared Clipped ReLU) 用定数
// =============================================================================

/// SCReLU 量子化係数 (bullet-shogi 準拠)
///
/// SCReLU では clamp(x, 0, QA)² を計算する。
/// QA = 127 のとき、最大出力は 127² = 16,129。
///
/// スケーリング設計:
/// - 入力: i16 (FeatureTransformer出力、範囲 [-QA, QA])
/// - 出力: i32 (最大 QA² = 16,129)
/// - オーバーフロー検証: 16,129 × 127 × 512 < i32_MAX ✓
pub const SCRELU_QA: i16 = 127;

/// SCReLU L1層以降の量子化係数 (bullet-shogi 準拠)
///
/// L1層以降では QB = 64 を使用。
/// i32 として定義(中間層の i32 演算で使用するため)
pub const SCRELU_QB: i32 = 64;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_constants() {
        assert_eq!(TRANSFORMED_FEATURE_DIMENSIONS, 256);
        assert_eq!(HIDDEN1_DIMENSIONS, 32);
        assert_eq!(HIDDEN2_DIMENSIONS, 32);
        assert_eq!(OUTPUT_DIMENSIONS, 1);
    }
}