aboutsummaryrefslogtreecommitdiff
path: root/include/nn.c
diff options
context:
space:
mode:
Diffstat (limited to 'include/nn.c')
-rw-r--r--include/nn.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/include/nn.c b/include/nn.c
new file mode 100644
index 0000000..705f258
--- /dev/null
+++ b/include/nn.c
@@ -0,0 +1,122 @@
+/*
+ This file is part of ctak.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ctak. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "nn.h"
+
+#include "weights_5.h"
+#include "weights_6.h"
+
+// ===================================================================
+// Helpers
+// ===================================================================
+
+#define RELU(x) ((x) = ((x) < 0) ? 0 : (x))
+
+static float fast_tanh(const float x) {
+ // tanh via a degree-11 odd polynomial, least-squares fit of tanh on [-3, 3]
+ // (max absolute error ~4e-3), saturating to +-1 outside that interval.
+ if (x > 3.0f)
+ return 1.0f;
+ if (x < -3.0f)
+ return -1.0f;
+ const float x2 = x * x;
+ return x * (0.997136032f + x2 * (-0.305215124f + x2 *
+ (0.0832466371f + x2 *
+ (-0.0139947924f + x2 *
+ (0.00123106314f + x2 * -4.29748644e-05f)))));
+}
+// ===================================================================
+// Input encoding
+// ===================================================================
+
+inline uint8_t cell_idx_at(const tak_state_p state, const uint8_t loc) {
+ const uint8_t d = state->celldat[loc];
+ if ((d & NUM_MASK) == 0)
+ return 0; // empty
+ return (uint8_t)(1u + 3u * (state->colours[loc] & 1u) + (d & STONE_MASK));
+}
+
+inline void nn_cell_idxs(uint8_t *out, tak_state_p state) {
+ const unsigned int n_cells = state->board_size * state->board_size;
+ for (unsigned int loc = 0; loc < n_cells; loc++)
+ out[loc] = cell_idx_at(state, loc);
+}
+
+inline void nn_prepare_head(float out_head[3], tak_state_p state) {
+ const float inv_max_flats =
+ (state->board_size == 6) ? (1.0f / 30.0f) : (1.0f / 21.0f);
+ out_head[0] = (state->ply & 1) ? 1 : -1;
+ out_head[1] = (float)(state->white_count & 127) * inv_max_flats;
+ out_head[2] = (float)(state->black_count & 127) * inv_max_flats;
+}
+
+void nn_prepare_input(float *cur_board, tak_state_p state,
+ const float embedding[NN_N_EMB]) {
+ const unsigned int n_cells = state->board_size * state->board_size;
+ nn_prepare_head(cur_board, state);
+ uint8_t idxs[36];
+ nn_cell_idxs(idxs, state);
+ for (unsigned int loc = 0; loc < n_cells; loc++)
+ cur_board[3 + loc] = embedding[idxs[loc]];
+}
+
+// ===================================================================
+// Implementation of small neural networks
+// ===================================================================
+
+float eval_net(const float *board, const unsigned int n_inp,
+ const float *w, const float *biases,
+ const float *logit_w, const float logit_b,
+ const unsigned int n_dense) {
+ float dense1[FIVE_DENSE_NUM]; // FIVE_DENSE_NUM == SIX_DENSE_NUM
+ float z = logit_b;
+
+ for (unsigned int d1 = 0; d1 < n_dense; d1++) {
+ float h = biases[d1];
+ const float *wd = w + d1 * n_inp;
+ for (unsigned int fl = 0; fl < n_inp; fl++)
+ h += board[fl] * wd[fl];
+ RELU(h);
+ dense1[d1] = h;
+ }
+ for (unsigned int d1 = 0; d1 < n_dense; d1++)
+ z += dense1[d1] * logit_w[d1];
+
+ return fast_tanh(z);
+}
+
+// ===================================================================
+// Interfaces to the two built-in evaluators
+// ===================================================================
+
+float five_nn_fn(tak_state_p state) {
+ static float cur_board[FIVE_INP_NUM] NN_ALIGNED;
+
+ nn_prepare_input(cur_board, state, five_embedding);
+ return eval_net(cur_board, FIVE_INP_NUM, &five_dense_weights[0][0],
+ five_dense_biases, five_logit_weights, five_logit_bias,
+ FIVE_DENSE_NUM);
+}
+
+float six_nn_fn(tak_state_p state) {
+ static float cur_board[SIX_INP_NUM] NN_ALIGNED;
+
+ nn_prepare_input(cur_board, state, six_embedding);
+ return eval_net(cur_board, SIX_INP_NUM, &six_dense_weights[0][0],
+ six_dense_biases, six_logit_weights, six_logit_bias,
+ SIX_DENSE_NUM);
+}