in fbpcs/emp_games/lift/calculator/OutputMetrics.hpp [522:592]
void OutputMetrics<MY_ROLE>::calculateMatchCount(
const OutputMetrics::GroupType& groupType,
const std::vector<emp::Bit>& populationBits,
const std::vector<std::vector<emp::Integer>>& purchaseValueArrays) {
XLOG(INFO) << "Calculate " << getGroupTypeStr(groupType) << " MatchCount";
// a valid test/control match is when a person with an opportunity who made
// ANY nonzero conversion. Therefore we can just check first if an opportunity
// is valid, then bitwise AND this with the bitwise OR over all purchases (to
// check for purchases). This gets us a binary indication if a user is
// matched with any opportunity
XLOG(INFO) << "Share opportunity timestamps";
const std::vector<emp::Integer> opportunityTimestamps =
privatelyShareIntsFromPublisher<MY_ROLE>(
inputData_.getOpportunityTimestamps(), n_, QUICK_BITS);
XLOG(INFO) << "Share purchase timestamps";
const std::vector<std::vector<emp::Integer>> purchaseTimestampArrays =
privatelyShareIntArraysFromPartner<MY_ROLE>(
inputData_.getPurchaseTimestampArrays(),
n_, /* numVals */
numConversionsPerUser_ /* arraySize */,
QUICK_BITS /* bitLen */);
auto matchArrays = private_measurement::functional::zip_apply(
[](emp::Bit isUser,
emp::Integer opportunityTimestamp,
std::vector<emp::Integer> purchaseTimestampArray) -> emp::Bit {
const emp::Integer zero = emp::Integer{
static_cast<int>(opportunityTimestamp.size()), 0, emp::PUBLIC};
emp::Bit validOpportunity =
(isUser &
(opportunityTimestamp > zero)); // check if opportunity is valid
emp::Bit isUserMatched = emp::Bit{0, emp::PUBLIC};
for (const auto& purchaseTS : purchaseTimestampArray) {
// check for the existence of a valid purchase
isUserMatched = isUserMatched | (purchaseTS > zero);
}
return isUserMatched & validOpportunity;
},
populationBits.begin(),
populationBits.end(),
opportunityTimestamps.begin(),
purchaseTimestampArrays.begin());
if (groupType == GroupType::TEST) {
metrics_.testMatchCount = sum(matchArrays);
} else {
metrics_.controlMatchCount = sum(matchArrays);
}
// And compute for breakdowns + cohorts
// TODO: These could be abstracted into a common function
for (size_t i = 0; i < numPublisherBreakdowns_; ++i) {
auto groupBits = private_measurement::secret_sharing::multiplyBitmask(
matchArrays, publisherBitmasks_.at(i));
if (groupType == GroupType::TEST) {
publisherBreakdowns_[i].testMatchCount = sum(groupBits);
} else {
publisherBreakdowns_[i].controlMatchCount = sum(groupBits);
}
}
for (size_t i = 0; i < numPartnerCohorts_; ++i) {
auto groupBits = private_measurement::secret_sharing::multiplyBitmask(
matchArrays, partnerBitmasks_.at(i));
if (groupType == GroupType::TEST) {
cohortMetrics_[i].testMatchCount = sum(groupBits);
} else {
cohortMetrics_[i].controlMatchCount = sum(groupBits);
}
}
}