- // Calculate cumulative value without using difference calculation
- void RefreshAccumulator(const Position& pos) const {
-
- auto& accumulator = pos.state()->accumulator;
- IndexType i = 0;
- Features::IndexList active_indices[2];
- RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i],
- active_indices);
- for (Color perspective : { WHITE, BLACK }) {
- #ifdef TILING
- for (unsigned j = 0; j < kHalfDimensions / kTileHeight; ++j) {
- auto biasesTile = reinterpret_cast<const vec_t*>(
- &biases_[j * kTileHeight]);
+ void update_accumulator(const Position& pos, const Color perspective) const {
+
+ // The size must be enough to contain the largest possible update.
+ // That might depend on the feature set and generally relies on the
+ // feature set's update cost calculation to be correct and never
+ // allow updates with more added/removed features than MaxActiveDimensions.
+ using IndexList = ValueList<IndexType, FeatureSet::MaxActiveDimensions>;
+
+ #ifdef VECTOR
+ // Gcc-10.2 unnecessarily spills AVX2 registers if this array
+ // is defined in the VECTOR code below, once in each branch
+ vec_t acc[NumRegs];
+ #endif
+
+ // Look for a usable accumulator of an earlier position. We keep track
+ // of the estimated gain in terms of features to be added/subtracted.
+ StateInfo *st = pos.state(), *next = nullptr;
+ int gain = FeatureSet::refresh_cost(pos);
+ while (st->accumulator.state[perspective] == EMPTY)
+ {
+ // This governs when a full feature refresh is needed and how many
+ // updates are better than just one full refresh.
+ if ( FeatureSet::requires_refresh(st, perspective)
+ || (gain -= FeatureSet::update_cost(st) + 1) < 0)
+ break;
+ next = st;
+ st = st->previous;
+ }
+
+ if (st->accumulator.state[perspective] == COMPUTED)
+ {
+ if (next == nullptr)
+ return;
+
+ // Update incrementally in two steps. First, we update the "next"
+ // accumulator. Then, we update the current accumulator (pos.state()).
+
+ // Gather all features to be updated.
+ const Square ksq = pos.square<KING>(perspective);
+ IndexList removed[2], added[2];
+ FeatureSet::append_changed_indices(
+ ksq, next, perspective, removed[0], added[0]);
+ for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
+ FeatureSet::append_changed_indices(
+ ksq, st2, perspective, removed[1], added[1]);
+
+ // Mark the accumulators as computed.
+ next->accumulator.state[perspective] = COMPUTED;
+ pos.state()->accumulator.state[perspective] = COMPUTED;
+
+ // Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
+ StateInfo *states_to_update[3] =
+ { next, next == pos.state() ? nullptr : pos.state(), nullptr };
+ #ifdef VECTOR
+ for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
+ {
+ // Load accumulator