#include #include // tanh関数を使うために必要 #include "params.h" // Pythonで生成したヘッダファイルを読み込み // グローバル変数: リザバーの状態ベクトル (初期値は全部0) float reservoir_state[N_NEURONS] = {0}; // --- 関数定義: リザバーの状態を更新する --- // Pythonでの x = tanh(W_in*u + W_res*x) をC言語で実装 void update_reservoir(float input_val) { float next_state[N_NEURONS]; // 次の状態を一時保存するバッファ for (int i = 0; i < N_NEURONS; i++) { float activation = 0.0f; // 1. 入力の寄与 (W_in * u) activation += W_in[i] * input_val; // 2. リザバー内部の反響 (W_res * x_prev) // W_res は1次元配列としてフラット化されているので、インデックス計算が必要です for (int j = 0; j < N_NEURONS; j++) { // 行列の (i, j) 要素へのアクセス: i * 列数 + j activation += W_res[i * N_NEURONS + j] * reservoir_state[j]; } // 3. 非線形変換 (tanh) // マイコンによっては tanhf (float版) の方が高速です next_state[i] = tanhf(activation); } // 計算が終わったら、グローバル変数を更新 for (int i = 0; i < N_NEURONS; i++) { reservoir_state[i] = next_state[i]; } } // --- 関数定義: 推論を実行する --- float inference(float input_val) { // Step 1: リザバーの状態を更新 (ここは全ニューロン計算) update_reservoir(input_val); // Step 2: 出力計算 (★ここがスパース化で高速化された部分!) // 全50個ではなく、有効な5個(N_ACTIVE_WEIGHTS)だけ計算します float output = 0.0f; for (int i = 0; i < N_ACTIVE_WEIGHTS; i++) { int idx = W_out_idx[i]; // 何番目のニューロンか float w = W_out_val[i]; // 重みはいくらか // y = Σ w_i * x_i output += w * reservoir_state[idx]; } return output; } // --- メイン関数: 動作テスト --- int main() { printf("=== Reservoir Computing on C (Embedded Demo) ===\n"); printf("Total Neurons: %d\n", N_NEURONS); printf("Active Weights: %d (Sparse Ratio: %.1f%%)\n", N_ACTIVE_WEIGHTS, (float)N_ACTIVE_WEIGHTS / N_NEURONS * 100.0); printf("------------------------------------------------\n"); // テスト: 適当な入力値を入れて動かしてみる // (実際はここにセンサの値が入ります) float test_input = 0.5f; for (int t = 0; t < 10; t++) { // 推論実行 float result = inference(test_input); printf("Step %d | Input: %.4f -> Output: %.6f\n", t, test_input, result); // 次のステップのために、出力を入力に戻してみる(閉ループテスト) // ※Mackey-Glassのような時系列予測では、予測値を次の入力に使うことがあります test_input = result; } return 0; }