1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! 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幅(バイト)
pub const SIMD_WIDTH: usize = 32;
pub const SIMD_WIDTH: usize = 16;
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 * 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;