Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stdev ownership #500

Merged
merged 8 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions cpp/command/gtp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ struct GTPEngine {
int minMoves = 0;
int maxMoves = 10000000;
bool showOwnership = false;
bool showOwnershipStdev = false;
bool showPVVisits = false;
double secondsPerReport = TimeControls::UNLIMITED_TIME_DEFAULT;
vector<int> avoidMoveUntilByLocBlack;
Expand Down Expand Up @@ -690,8 +691,15 @@ struct GTPEngine {
if(buf.size() <= 0)
return;

vector<double> ownership;
if(args.showOwnership) {
vector<double> ownership, ownershipStdev;
if(args.showOwnershipStdev) {
static constexpr int64_t ownershipStdevMinVisits = 3;
tuple<vector<double>,vector<double>> ownershipAverageAndStdev;
ownershipAverageAndStdev = search->getAverageAndStandardDeviationTreeOwnership(ownershipStdevMinVisits);
ownership = std::get<0>(ownershipAverageAndStdev);
ownershipStdev = std::get<1>(ownershipAverageAndStdev);
}
else if(args.showOwnership) {
static constexpr int64_t ownershipMinVisits = 3;
ownership = search->getAverageTreeOwnership(ownershipMinVisits);
}
Expand Down Expand Up @@ -767,6 +775,19 @@ struct GTPEngine {
}
}

if(args.showOwnershipStdev) {
out << " ";

out << "ownershipStdev";
int nnXLen = search->nnXLen;
for(int y = 0; y<board.y_size; y++) {
for(int x = 0; x<board.x_size; x++) {
int pos = NNPos::xyToPos(x,y,nnXLen);
out << " " << ownershipStdev[pos];
}
}
}

cout << out.str() << endl;
};
}
Expand Down Expand Up @@ -835,7 +856,7 @@ struct GTPEngine {
bot->setAvoidMoveUntilByLoc(args.avoidMoveUntilByLocBlack,args.avoidMoveUntilByLocWhite);
if(args.analyzing) {
std::function<void(const Search* search)> callback = getAnalyzeCallback(pla,args);
if(args.showOwnership)
if(args.showOwnership || args.showOwnershipStdev)
bot->setAlwaysIncludeOwnerMap(true);
else
bot->setAlwaysIncludeOwnerMap(false);
Expand Down Expand Up @@ -1068,7 +1089,7 @@ struct GTPEngine {

std::function<void(const Search* search)> callback = getAnalyzeCallback(pla,args);
bot->setAvoidMoveUntilByLoc(args.avoidMoveUntilByLocBlack,args.avoidMoveUntilByLocWhite);
if(args.showOwnership)
if(args.showOwnership || args.showOwnershipStdev)
bot->setAlwaysIncludeOwnerMap(true);
else
bot->setAlwaysIncludeOwnerMap(false);
Expand Down Expand Up @@ -1261,6 +1282,7 @@ static GTPEngine::AnalyzeArgs parseAnalyzeCommand(
int minMoves = 0;
int maxMoves = 10000000;
bool showOwnership = false;
bool showOwnershipStdev = false;
bool showPVVisits = false;
vector<int> avoidMoveUntilByLocBlack;
vector<int> avoidMoveUntilByLocWhite;
Expand All @@ -1280,6 +1302,7 @@ static GTPEngine::AnalyzeArgs parseAnalyzeCommand(
//minmoves <int min number of moves to show>
//maxmoves <int max number of moves to show>
//ownership <bool whether to show ownership or not>
//ownershipStdev <bool whether to show ownershipStdev or not>
//pvVisits <bool whether to show pvVisits or not>

//Parse optional player
Expand Down Expand Up @@ -1382,6 +1405,9 @@ static GTPEngine::AnalyzeArgs parseAnalyzeCommand(
else if(isKata && key == "ownership" && Global::tryStringToBool(value,showOwnership)) {
continue;
}
else if(isKata && key == "ownershipStdev" && Global::tryStringToBool(value,showOwnershipStdev)) {
continue;
}
else if(isKata && key == "pvVisits" && Global::tryStringToBool(value,showPVVisits)) {
continue;
}
Expand All @@ -1399,6 +1425,7 @@ static GTPEngine::AnalyzeArgs parseAnalyzeCommand(
args.minMoves = minMoves;
args.maxMoves = maxMoves;
args.showOwnership = showOwnership;
args.showOwnershipStdev = showOwnershipStdev;
args.showPVVisits = showPVVisits;
args.avoidMoveUntilByLocBlack = avoidMoveUntilByLocBlack;
args.avoidMoveUntilByLocWhite = avoidMoveUntilByLocWhite;
Expand Down
6 changes: 5 additions & 1 deletion cpp/search/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ struct Search {
//or changing parameters or clearing search.
//If node is not providied, defaults to using the root node.
std::vector<double> getAverageTreeOwnership(double minWeight, const SearchNode* node = NULL) const;
std::tuple<std::vector<double>,std::vector<double>> getAverageAndStandardDeviationTreeOwnership(double minWeight, const SearchNode* node = NULL) const;

//Get ownership map as json
nlohmann::json getJsonOwnershipMap(const Player pla, const Player perspective, const Board& board, const SearchNode* node, double ownershipMinWeight) const;
Expand Down Expand Up @@ -649,7 +650,10 @@ struct Search {
std::string& prefix, int64_t origVisits, int depth, const AnalysisData& data, Player perspective
) const;

double getAverageTreeOwnershipHelper(std::vector<double>& accum, double minWeight, double desiredWeight, const SearchNode* node) const;
template<typename Func>
double traverseTreeWithOwnershipAndSelfWeight(Func&& averaging, double minWeight, double desiredWeight, const SearchNode* node) const;
template<typename Func>
double traverseTreeWithOwnershipAndSelfWeightHeler(Func&& averaging, double minWeight, double desiredWeight, double thisNodeWeight, const SearchChildPointer* children, double* childWeightBuf, int childrenCapacity) const;

};

Expand Down
58 changes: 46 additions & 12 deletions cpp/search/searchresults.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,11 +1217,34 @@ vector<double> Search::getAverageTreeOwnership(double minWeight, const SearchNod
if(!alwaysIncludeOwnerMap)
throw StringError("Called Search::getAverageTreeOwnership when alwaysIncludeOwnerMap is false");
vector<double> vec(nnXLen*nnYLen,0.0);
getAverageTreeOwnershipHelper(vec,minWeight,1.0,node);
traverseTreeWithOwnershipAndSelfWeight([&vec,this](float* ownership, double selfWeight){
for (int pos = 0; pos < nnXLen*nnYLen; pos++)
vec[pos] += selfWeight * ownership[pos];
},minWeight,1.0,node);
return vec;
}

double Search::getAverageTreeOwnershipHelper(vector<double>& accum, double minWeight, double desiredWeight, const SearchNode* node) const {
tuple<vector<double>,vector<double>> Search::getAverageAndStandardDeviationTreeOwnership(double minWeight, const SearchNode* node) const {
if(node == NULL)
node = rootNode;
vector<double> average(nnXLen*nnYLen,0.0);
vector<double> stdev(nnXLen*nnYLen,0.0);
traverseTreeWithOwnershipAndSelfWeight([&average,&stdev,this](float* ownership, double selfWeight) {
for (int pos = 0; pos < nnXLen*nnYLen; pos++) {
const double value = ownership[pos];
average[pos] += selfWeight * value;
stdev[pos] += selfWeight * value * value;
}
},minWeight,1.0,node);
for(int pos = 0; pos<nnXLen*nnYLen; pos++) {
const double avg = average[pos];
stdev[pos] = sqrt(max(stdev[pos] - avg * avg, 0.0));
}
return make_tuple(average, stdev);
}

template<typename Func>
double Search::traverseTreeWithOwnershipAndSelfWeight(Func&& averaging, double minWeight, double desiredWeight, const SearchNode* node) const {
if(node == NULL)
return 0;

Expand All @@ -1232,8 +1255,25 @@ double Search::getAverageTreeOwnershipHelper(vector<double>& accum, double minWe
int childrenCapacity;
const SearchChildPointer* children = node->getChildren(childrenCapacity);

vector<double> childWeightBuf(childrenCapacity);
double actualWeightFromChildren;
double thisNodeWeight = computeWeightFromNNOutput(nnOutput);
if (childrenCapacity <= 8) {
double childWeightBuf[8];
actualWeightFromChildren = traverseTreeWithOwnershipAndSelfWeightHeler(averaging, minWeight, desiredWeight, thisNodeWeight, children, childWeightBuf, childrenCapacity);
} else {
vector<double> childWeightBuf(childrenCapacity);
actualWeightFromChildren = traverseTreeWithOwnershipAndSelfWeightHeler(averaging, minWeight, desiredWeight, thisNodeWeight, children, &childWeightBuf[0], childrenCapacity);
}

double selfWeight = desiredWeight - actualWeightFromChildren;
float* ownerMap = nnOutput->whiteOwnerMap;
assert(ownerMap != NULL);
averaging(ownerMap, selfWeight);
return desiredWeight;
}

template<typename Func>
double Search::traverseTreeWithOwnershipAndSelfWeightHeler(Func&& averaging, double minWeight, double desiredWeight, double thisNodeWeight, const SearchChildPointer* children, double* childWeightBuf, int childrenCapacity) const {
int numChildren = 0;
for(int i = 0; i<childrenCapacity; i++) {
const SearchNode* child = children[i].getIfAllocated();
Expand Down Expand Up @@ -1265,16 +1305,10 @@ double Search::getAverageTreeOwnershipHelper(vector<double>& accum, double minWe
const SearchNode* child = children[i].getIfAllocated();
assert(child != NULL);
double desiredWeightFromChild = (double)childWeight * childWeight / relativeChildrenWeightSum * desiredWeightFromChildren;
actualWeightFromChildren += getAverageTreeOwnershipHelper(accum,minWeight,desiredWeightFromChild,child);
actualWeightFromChildren += traverseTreeWithOwnershipAndSelfWeight(averaging,minWeight,desiredWeightFromChild,child);
}

double selfWeight = desiredWeight - actualWeightFromChildren;
float* ownerMap = nnOutput->whiteOwnerMap;
assert(ownerMap != NULL);
for(int pos = 0; pos<nnXLen*nnYLen; pos++)
accum[pos] += selfWeight * ownerMap[pos];

return desiredWeight;

return actualWeightFromChildren;
}

json Search::getJsonOwnershipMap(const Player pla, const Player perspective, const Board& board, const SearchNode* node, double ownershipMinWeight) const {
Expand Down