static void AddTreeCtrs(const TTrainData& data, const TSplitTree& currentTree, TFold* fold, TLearnContext* ctx, TStatsFromPrevTree* statsFromPrevTree, TCandidateList* candList) { using TSeenProjHash = THashSet<TProjection>; TSeenProjHash seenProj; // greedy construction TProjection binAndOneHotFeaturesTree; binAndOneHotFeaturesTree.BinFeatures = currentTree.GetBinFeatures(); binAndOneHotFeaturesTree.OneHotFeatures = currentTree.GetOneHotFeatures(); seenProj.insert(binAndOneHotFeaturesTree); for (const auto& ctrSplit : currentTree.GetCtrSplits()) { seenProj.insert(ctrSplit.Projection); } TSeenProjHash addedProjHash; for (const auto& baseProj : seenProj) { if (baseProj.IsEmpty()) { continue; } for (int cf = 0; cf < data.AllFeatures.CatFeatures.ysize(); ++cf) { if (data.AllFeatures.CatFeatures[cf].empty() || data.AllFeatures.IsOneHot[cf] || ctx->Rand.GenRandReal1() > ctx->Params.ObliviousTreeOptions->Rsm) { continue; } TProjection proj = baseProj; proj.AddCatFeature(cf); if (proj.IsRedundant() || proj.GetFullProjectionLength() > ctx->Params.CatFeatureParams->MaxTensorComplexity) { continue; } if (addedProjHash.has(proj)) { continue; } addedProjHash.insert(proj); AddCtrsToCandList(*fold, *ctx, proj, candList); fold->GetCtrRef(proj); } } THashSet<TSplitCandidate> candidatesToErase; for (auto& splitCandidate : statsFromPrevTree->Stats) { if (splitCandidate.first.Type == ESplitType::OnlineCtr) { if (!addedProjHash.has(splitCandidate.first.Ctr.Projection)) { candidatesToErase.insert(splitCandidate.first); } } } for (const auto& splitCandidate : candidatesToErase) { statsFromPrevTree->Stats.erase(splitCandidate); } }