| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package privacy |
| |
| import ( |
| "config" |
| "fmt" |
| "math" |
| ) |
| |
| type ErrorCalculator struct { |
| ParamsCalc PrivacyEncodingParamsCalculator |
| } |
| |
| // Public factory method for creating an ErrorCalculator given a |
| // PrivacyEncodingParamsCalculator. |
| func NewErrorCalculator(paramsCalc PrivacyEncodingParamsCalculator) *ErrorCalculator { |
| return &ErrorCalculator{paramsCalc} |
| } |
| |
| // Public factory method for creating an ErrorCalculator given the file path |
| // of the PrivacyEncodingParams. |
| func NewErrorCalculatorFromPrivacyParams(privacyParamsPath string) (*ErrorCalculator, error) { |
| paramsCalculator, err := NewPrivacyEncodingParamsCalculator(privacyParamsPath) |
| if err != nil { |
| return nil, err |
| } |
| errorCalculator := NewErrorCalculator(*paramsCalculator) |
| if err != nil { |
| return nil, err |
| } |
| return errorCalculator, nil |
| } |
| |
| // Given a |metric|, |report|, and |params|, estimates the report row error. |
| // |
| // TODO(jaredweinstein): implement estimates for other report types. |
| func (e *ErrorCalculator) Estimate(metric *config.MetricDefinition, report *config.ReportDefinition, epsilon float64, population uint64) (estimate float64, err error) { |
| reportType := report.GetReportType() |
| |
| sparsity, err := getSparsityForReport(metric, report) |
| if err != nil { |
| return -1, err |
| } |
| |
| populationConstant := e.ParamsCalc.Constants.population |
| privacyEncodingParams, err := e.ParamsCalc.GetPrivacyEncodingParams(epsilon, populationConstant, sparsity) |
| if err != nil { |
| return -1, err |
| } |
| |
| var errorEstimate float64 |
| switch reportType { |
| case config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS: |
| fallthrough // Calculate RMSE Error for each bucket. |
| case config.ReportDefinition_UNIQUE_DEVICE_COUNTS: |
| errorEstimate = SingleContributionRapporRMSE(population, privacyEncodingParams) |
| case config.ReportDefinition_HOURLY_VALUE_HISTOGRAMS: |
| // (24 * population) pseudo-users to account for hourly values. |
| errorEstimate = SingleContributionRapporRMSE(24*population, privacyEncodingParams) |
| case config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS: |
| errorEstimate = MultipleContributionRapporRMSE(population, privacyEncodingParams, report.MaxCount) |
| case config.ReportDefinition_FLEETWIDE_HISTOGRAMS: |
| errorEstimate = MultipleContributionRapporRMSE(population, privacyEncodingParams, 1) |
| default: |
| reportType := config.ReportDefinition_ReportType_name[int32(reportType)] |
| return -1, fmt.Errorf("Error estimation is not supported for reports of type %s", reportType) |
| } |
| |
| if math.IsNaN(errorEstimate) || math.IsInf(errorEstimate, 0) { |
| return errorEstimate, fmt.Errorf("Error estimation failed to return valid result due to an invalid or missing field.") |
| } |
| return errorEstimate, nil |
| } |
| |
| // Compute the 2D-RAPPOR RMSE for reports with single contributions. |
| // |
| // See Proposition 1 and 2 of go/histogram-aggregation-privacy-guarantee. |
| func SingleContributionRapporRMSE(population uint64, privacyParams PrivacyEncodingParams) float64 { |
| probBitFlip := privacyParams.ProbBitFlip |
| return math.Sqrt(float64(population)*probBitFlip*(1-probBitFlip)) / (1 - 2*probBitFlip) |
| } |
| |
| // Compute 2D-RAPPOR RMSE for reports with multiple or real-number contributions. |
| // |
| // See Proposition 1, 2, and 3 of go/histogram-aggregation-privacy-guarantee. |
| func MultipleContributionRapporRMSE(population uint64, privacyParams PrivacyEncodingParams, maxUserContribution uint64) float64 { |
| var n = float64(population) |
| var p = privacyParams.ProbBitFlip |
| var m = float64(maxUserContribution) |
| var r = float64(privacyParams.NumIndexPoints) |
| |
| var estimate = float64(n) * p * (1 - p) / math.Pow((1-2*p), 2) |
| estimate = estimate * (2*math.Pow(r, 3) + 3*math.Pow(r, 2) + r) / 6 |
| estimate = estimate + (n / 4) |
| estimate = m / r * math.Sqrt(estimate) |
| return estimate |
| } |