diff options
Diffstat (limited to 'include/nn.c')
| -rw-r--r-- | include/nn.c | 122 |
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); +} |
