Error Calculator supports NumericStats report types
Added back support for UniqueDeviceNumericStats and
HourlyValueNumericStats. All Cobalt 1.1 report types are now supported.
Change-Id: I2acecff91ec5c1a5cdb61fc09439d1ad84d5489a
Reviewed-on: https://fuchsia-review.googlesource.com/c/cobalt/+/487278
Commit-Queue: Jared Weinstein <jaredweinstein@google.com>
Reviewed-by: Pasin Manurangsi <pasin@google.com>
Reviewed-by: Laura Peskin <pesk@google.com>
diff --git a/src/bin/config_parser/src/privacy/error_calculator.go b/src/bin/config_parser/src/privacy/error_calculator.go
index 4ca2458..acae241 100644
--- a/src/bin/config_parser/src/privacy/error_calculator.go
+++ b/src/bin/config_parser/src/privacy/error_calculator.go
@@ -74,18 +74,17 @@
contributionRange := report.MaxCount
population *= 24 // Hourly contribution interval: 24*population pseudo-users.
errorEstimate = multipleContributionRapporRMSE(population, probBitFlip, contributionRange, discretization)
- // TODO(jaredweinstein): Enable NumericStats once the formula is resolved.
- // case config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS:
- // if err := meanReportConfigurationError(report, minDenominatorEstimate); err != nil {
- // return -1, err
- // }
- // errorEstimate = NumericStatsRapporRMSE(population, privacyEncodingParams, minDenominatorEstimate, report)
- // case config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS:
- // if err := meanReportConfigurationError(report, minDenominatorEstimate); err != nil {
- // return -1, err
- // }
- // population *= 24 // Hourly contribution interval: 24*population pseudo-users.
- // errorEstimate = NumericStatsRapporRMSE(population, privacyEncodingParams, minDenominatorEstimate, report)
+ case config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS:
+ if err := meanReportConfigurationError(report, minDenominatorEstimate); err != nil {
+ return -1, err
+ }
+ errorEstimate = numericStatsRapporRMSE(population, probBitFlip, minDenominatorEstimate, discretization, report)
+ case config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS:
+ if err := meanReportConfigurationError(report, minDenominatorEstimate); err != nil {
+ return -1, err
+ }
+ population *= 24 // Hourly contribution interval: 24*population pseudo-users.
+ errorEstimate = numericStatsRapporRMSE(population, probBitFlip, minDenominatorEstimate, discretization, report)
case config.ReportDefinition_FLEETWIDE_MEANS:
if err := meanReportConfigurationError(report, minDenominatorEstimate); err != nil {
return -1, err
@@ -126,23 +125,18 @@
return estimate
}
-// TODO(jaredweinstein): Add back NumericStats once the formula is resolved.
// Compute 2D-RAPPOR RMSE for NumericStats reports.
-// func NumericStatsRapporRMSE(population uint64, params PrivacyEncodingParams, minDenominatorEstimate uint64, report *config.ReportDefinition) float64 {
-// sigma := SingleContributionRapporRMSE(population, params)
-// a := params.ProbBitFlip / (1 - 2*params.ProbBitFlip)
-// M := uint64(math.Max(math.Abs(float64(report.MinValue)), math.Abs(float64(report.MaxValue))))
-//
-// // Numerator is aggregated like FleetwideOccurrenceCounts.
-// rmseNumerator := MultipleContributionRapporRMSE(population, params, M)
-// mseNumerator := math.Pow(rmseNumerator, 2)
-//
-// // Denominator is aggregated like UniqueDeviceCounts.
-// rmseDenominator := SingleContributionRapporRMSE(population, params)
-// mseDenominator := math.Pow(rmseDenominator, 2)
-//
-// return meanRapporRMSE(mseNumerator, mseDenominator, sigma, a, float64(M), float64(minDenominatorEstimate))
-// }
+func numericStatsRapporRMSE(population uint64, probBitFlip float64, minDenominatorEstimate uint64, discretization uint64, report *config.ReportDefinition) float64 {
+ T := float64(math.Max(float64(-report.MinValue), float64(report.MaxValue)))
+ M := uint64(report.MaxValue - report.MinValue)
+
+ sigmaNum := multipleContributionRapporRMSE(population, probBitFlip, M, discretization)
+ alphaNum := probBitFlip * float64(M) / (1 - 2*probBitFlip)
+ sigmaDenom := singleContributionRapporRMSE(population, probBitFlip)
+ alphaDenom := probBitFlip / (1 - 2*probBitFlip)
+
+ return eThree(T, float64(minDenominatorEstimate), sigmaNum, alphaNum, sigmaDenom, alphaDenom)
+}
// Compute 2D-RAPPOR RMSE for the FleetwideMeans report.
func fleetwideMeansRapporRMSE(population uint64, probBitFlip float64, minDenominatorEstimate uint64, discretization uint64, report *config.ReportDefinition) float64 {
@@ -190,6 +184,21 @@
return -quad.Fixed(f, l-B, 0, 10000, nil, 0)
}
+func eThree(T float64, B float64, sigmaNum float64, alphaNum float64, sigmaDenom float64, alphaDenom float64) float64 {
+ f := func(y float64) float64 {
+ tOne := math.Sqrt(y) / (T + 1)
+ tTwo := math.Sqrt(y) * B / (math.Sqrt(y) + T + 1)
+ t := math.Max(tOne, tTwo)
+ return 2*math.Exp(-math.Pow(sigmaNum, 2)/math.Pow(alphaNum, 2)*
+ h(alphaNum*t/math.Pow(sigmaNum, 2))) +
+ 2*math.Exp(-math.Pow(sigmaDenom, 2)/math.Pow(alphaDenom, 2)*
+ h(alphaDenom*t/math.Pow(sigmaDenom, 2)))
+
+ }
+ return quad.Fixed(f, 0, math.Inf(1), 10000, nil, 0)
+
+}
+
func h(u float64) float64 {
return (1+u)*math.Log(1+u) - u
}
diff --git a/src/bin/config_parser/src/privacy/error_calculator_test.go b/src/bin/config_parser/src/privacy/error_calculator_test.go
index b2a85f8..a73c4da 100644
--- a/src/bin/config_parser/src/privacy/error_calculator_test.go
+++ b/src/bin/config_parser/src/privacy/error_calculator_test.go
@@ -77,20 +77,22 @@
LocalAggregationProcedure: config.ReportDefinition_SELECT_FIRST,
MaxCount: 20,
}
- // uniqueDeviceNumericStats := config.ReportDefinition{
- // ReportName: "UniqueDeviceNumericStats",
- // ReportType: config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
- // MaxValue: 1,
- // }
- // hourlyValueNumericStats := config.ReportDefinition{
- // ReportName: "HourlyDeviceNumericStats",
- // ReportType: config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS,
- // MaxValue: 1,
- // }
- // uniqueDeviceNumericStatsMissingMaxValue := config.ReportDefinition{
- // ReportName: "UniqueDeviceNumericStatsMissingMaxValue",
- // ReportType: config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
- // }
+ uniqueDeviceNumericStats := config.ReportDefinition{
+ ReportName: "UniqueDeviceNumericStats",
+ ReportType: config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
+ MinValue: 0,
+ MaxValue: 1,
+ }
+ hourlyValueNumericStats := config.ReportDefinition{
+ ReportName: "HourlyValueNumericStats",
+ ReportType: config.ReportDefinition_HOURLY_VALUE_NUMERIC_STATS,
+ MinValue: 0,
+ MaxValue: 1,
+ }
+ uniqueDeviceNumericStatsMissingMaxValue := config.ReportDefinition{
+ ReportName: "UniqueDeviceNumericStatsMissingMaxValue",
+ ReportType: config.ReportDefinition_UNIQUE_DEVICE_NUMERIC_STATS,
+ }
fleetwideMeans := config.ReportDefinition{
ReportName: "FleetwideMeans",
ReportType: config.ReportDefinition_FLEETWIDE_MEANS,
@@ -148,14 +150,14 @@
{args{&testMetric, &fleetwideHistogramHighMax, 1, 10000, 0}, true, 1057.87447719305},
// Mean reports
- // {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 500}, true, 0.027891803930152177},
- // {args{&testMetric, &uniqueDeviceNumericStats, 10, 10000, 500}, true, 0.018650077285590205},
- // {args{&testMetric, &uniqueDeviceNumericStats, 1, 20000, 500}, true, 0.039654432133364455},
- // {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 2000}, true, 0.006907368191507632},
- // {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 500}, true, 0.14403990551821316},
- // {args{&testMetric, &hourlyValueNumericStats, 10, 10000, 500}, true, 0.09424308127635954},
- // {args{&testMetric, &hourlyValueNumericStats, 1, 20000, 500}, true, 0.20996453488325503},
- // {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 2000}, true, 0.03425744055252169},
+ {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 500}, true, 0.05322079602809264},
+ {args{&testMetric, &uniqueDeviceNumericStats, 10, 10000, 500}, true, 0.05052783827668571},
+ {args{&testMetric, &uniqueDeviceNumericStats, 1, 20000, 500}, true, 0.11687754910096394},
+ {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 2000}, true, 0.0028519041732824315},
+ {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 2000}, true, 0.08391065449924393},
+ {args{&testMetric, &hourlyValueNumericStats, 10, 10000, 2000}, true, 0.07965661465979317},
+ {args{&testMetric, &hourlyValueNumericStats, 1, 20000, 2000}, true, 0.18931407076500353},
+ {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 5000}, true, 0.011480997122055539},
{args{&testMetric, &fleetwideMeans, 1, 10000, 500}, true, 1140.9529271415656},
{args{&testMetric, &fleetwideMeans, 10, 10000, 500}, true, 819.2029589502301},
@@ -169,9 +171,9 @@
{args{&testMetric, &uniqueDeviceCount, 0, 100000, 0}, true, 0},
// Invalid input
- // {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 0}, false, 0},
- // {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 0}, false, 0},
- // {args{&testMetric, &uniqueDeviceNumericStatsMissingMaxValue, 1, 10000, 500}, false, 0},
+ {args{&testMetric, &uniqueDeviceNumericStats, 1, 10000, 0}, false, 0},
+ {args{&testMetric, &hourlyValueNumericStats, 1, 10000, 0}, false, 0},
+ {args{&testMetric, &uniqueDeviceNumericStatsMissingMaxValue, 1, 10000, 500}, false, 0},
{args{&testMetric, &fleetwideMeansMissingMaxValue, 1, 10000, 500}, false, 0},
// This report type is not currently supported.