Showing with 58 additions and 1 deletion.
  1. +19 −1 src/search.cpp
  2. +30 −0 src/thread.cpp
  3. +6 −0 src/thread.h
  4. +3 −0 src/types.h
20 changes: 19 additions & 1 deletion src/search.cpp
Expand Up @@ -559,7 +559,8 @@ namespace {
bool ttHit, ttPv, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece;
int moveCount, captureCount, quietCount;
int moveCount, captureCount, quietCount, prediction;
float features[PercInput] = {0.0, 0.0, 0.0, 0.0};

// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
Expand Down Expand Up @@ -1056,10 +1057,27 @@ namespace {
r -= ss->statScore / 20000 * ONE_PLY;
}

// Predict using a perceptron
features[0] = float(improving);
features[1] = float(inCheck);
features[2] = float(captureOrPromotion) ;
features[3] = -float(cutNode);

prediction = thisThread->infer(features);

int threshold = 4500;
if (thisThread->perceptronAccuracy > threshold)
r -= prediction * ONE_PLY * (thisThread->perceptronAccuracy - threshold) / 100;

Depth d = std::max(newDepth - std::max(r, DEPTH_ZERO), ONE_PLY);

value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);

int result = (value > alpha) + (value > beta);

// Train the perceptron if needed
thisThread->train(features, 0.5e-2, prediction, result);

doFullDepthSearch = (value > alpha && d != newDepth);
}
else
Expand Down
30 changes: 30 additions & 0 deletions src/thread.cpp
Expand Up @@ -20,6 +20,8 @@

#include <algorithm> // For std::count
#include <cassert>
#include <cmath>


#include "movegen.h"
#include "search.h"
Expand Down Expand Up @@ -66,6 +68,13 @@ void Thread::clear() {
h->fill(0);

continuationHistory[NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);

perceptronAccuracy = 0;
perceptronWeights[PercInput] = 99.9;
for (int d1 = 0; d1 < PercInput; d1++)
{
perceptronWeights[d1] = -0.01;
}
}

/// Thread::start_searching() wakes up the thread that will start the search
Expand Down Expand Up @@ -117,6 +126,27 @@ void Thread::idle_loop() {
}
}

int Thread::infer(float input[PercInput]){

float x = perceptronWeights[PercInput]; // bias
for (int d = 0; d < PercInput; d++){
x += 1 / (exp(- perceptronWeights[d] * input[d]) + 1);
}
return int(x) / 100;
}

void Thread::train(float input[PercInput], float rate, int prediction, int result){

int error = (result - prediction);

perceptronAccuracy += 100 * (prediction == result);
perceptronAccuracy = 98 * perceptronAccuracy / 100;
perceptronWeights[PercInput] += rate * error;
for (int d = 0; d < PercInput; d++){
perceptronWeights[d] += input[d] * rate * error;
}
}

/// ThreadPool::set() creates/destroys threads to match the requested number.
/// Created and launched threads will immediately go to sleep in idle_loop.
/// Upon resizing, threads are recreated to allow for binding if necessary.
Expand Down
6 changes: 6 additions & 0 deletions src/thread.h
Expand Up @@ -57,6 +57,12 @@ class Thread {
void start_searching();
void wait_for_search_finished();

int infer(float input[PercInput]);
void train(float input[PercInput], float rate, int prediction, int result);

float perceptronWeights[PercInput + 1];
int perceptronAccuracy;

Pawns::Table pawnsTable;
Material::Table materialTable;
Endgames endgames;
Expand Down
3 changes: 3 additions & 0 deletions src/types.h
Expand Up @@ -253,6 +253,9 @@ enum Rank : int {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
};

enum Perceptron : int {
PercInput = 4,
};

/// Score enum stores a middlegame and an endgame value in a single integer (enum).
/// The least significant 16 bits are used to store the middlegame value and the
Expand Down