| // Copyright 2021 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 ( |
| "math" |
| "testing" |
| ) |
| |
| func TestGetPmfBinomialDistributionSimple(t *testing.T) { |
| pmf, err := getPmfBinomialDistribution(2, 0.5, 0) |
| if err != nil { |
| t.Errorf("getPmfBinomialDistributionibution returned error: %s", err.Error()) |
| return |
| } |
| expectedPmf := make(ProbabilityMassFunction) |
| expectedPmf[0] = 0.25 |
| expectedPmf[1] = 0.5 |
| expectedPmf[2] = 0.25 |
| if !deepEqualWithError(pmf, expectedPmf, 0.001) { |
| t.Errorf("Incorrect PMF computed by getPmfBinomialDistribution: expected %v, got %v", expectedPmf, pmf) |
| } |
| } |
| |
| func TestGetPmfBinomialDistributionHardWithTruncation(t *testing.T) { |
| truncation := 0.01 |
| pmf, err := getPmfBinomialDistribution(100, 0.5, truncation) |
| if err != nil { |
| t.Errorf("getPmfBinomialDistribution returned error: %s", err.Error()) |
| return |
| } |
| |
| error := 0.0001 |
| if !equalWithError(pmf[2], 0, error) || !equalWithError(pmf[40], 0.0108, error) || !equalWithError(pmf[51], 0.0780, error) || !equalWithError(pmf[90], 0, error) { |
| t.Errorf("Incorrect PMF computed by getPmfBinomialDistribution: expected values 2:0, 40: 0.0108, 51: 0.0780, 90: 0, got %v", pmf) |
| } |
| |
| sum := 0.0 |
| for _, prob := range pmf { |
| sum += prob |
| } |
| if equalWithError(sum+truncation, 1.0, 0.0001) { |
| t.Errorf("Incorrect PMF computed by getPmfBinomialDistribution: expected mass of the truncated distribution to be close to %f, got %f", 1.0-truncation, sum) |
| } |
| } |
| |
| func TestGetPmfShiftedBinomialDistributionSimple(t *testing.T) { |
| pmf, err := getPmfShiftedBinomialDistribution(2, 0.4, 0) |
| if err != nil { |
| t.Errorf("getPmfShiftedBinomialDistribution returned error: %s", err.Error()) |
| return |
| } |
| expectedPmf := make(ProbabilityMassFunction) |
| expectedPmf[0] = 0.24 |
| expectedPmf[1] = 0.52 |
| expectedPmf[2] = 0.24 |
| if !deepEqualWithError(pmf, expectedPmf, 0.001) { |
| t.Errorf("Incorrect PMF computed by getPmfBinomialDistribution: expected %v, got %v", expectedPmf, pmf) |
| } |
| } |
| |
| func TestGetPmfShiftedBinomialDistributionHardWithTruncation(t *testing.T) { |
| truncation := 0.01 |
| pmf, err := getPmfShiftedBinomialDistribution(100, 0.4, truncation) |
| if err != nil { |
| t.Errorf("getPmfShiftedBinomialDistribution returned error: %s", err.Error()) |
| return |
| } |
| |
| error := 0.001 |
| if !equalWithError(pmf[5], 0, error) || !equalWithError(pmf[39], 0.079, error) || !equalWithError(pmf[50], 0.0112, error) || !equalWithError(pmf[70], 0, error) { |
| t.Errorf("Incorrect PMF computed by getPmfBinomialDistribution: expected values 5:0, 39: 0.079, 50: 0.0112, 70: 0, got %v", pmf) |
| } |
| |
| sum := 0.0 |
| for _, prob := range pmf { |
| sum += prob |
| } |
| if equalWithError(sum+truncation, 1.0, 0.0001) { |
| t.Errorf("Incorrect PMF computed by getPMFBinomialDistribution: expected mass of the truncated distribution to be close to %f, got %f", 1.0-truncation, sum) |
| } |
| } |
| |
| func TestGetPrivacyLossDistribution(t *testing.T) { |
| discretization := 0.0001 |
| |
| expectedPmf := make(ProbabilityMassFunction) |
| expectedPmf[-8108] = 0.0092 |
| expectedPmf[-6060] = 0.0415 |
| expectedPmf[-4625] = 0.0261 |
| expectedPmf[-3482] = 0.0622 |
| expectedPmf[-2577] = 0.1175 |
| expectedPmf[-2047] = 0.0225 |
| expectedPmf[1] = 0.3149 |
| expectedPmf[2049] = 0.0276 |
| expectedPmf[2579] = 0.1521 |
| expectedPmf[3484] = 0.0881 |
| expectedPmf[4627] = 0.0415 |
| expectedPmf[6062] = 0.0760 |
| expectedPmf[8110] = 0.0207 |
| |
| resultPld, err := getPrivacyLossDistribution(3, 1, 0.4, 0.0, discretization) |
| if err != nil { |
| t.Errorf("getPrivacyLossDistribution returned error: %s", err.Error()) |
| return |
| } |
| |
| if !deepEqualWithError(expectedPmf, resultPld.pmf, 0.0001) { |
| t.Errorf("Incorrect result of getPrivacyLossDistribution without truncation: expected %v, got %v", expectedPmf, resultPld.pmf) |
| } |
| } |
| |
| func TestGetPrivacyLossDistributionForLargePopulationTotalMass(t *testing.T) { |
| truncation := 0.000001 |
| discretization := 0.001 |
| resultPld, err := getPrivacyLossDistribution(300000, 1000, 0.4, truncation, discretization) |
| if err != nil { |
| t.Errorf("getPrivacyLossDistribution returned error: %s", err.Error()) |
| return |
| } |
| sum := 0.0 |
| for _, prob := range resultPld.pmf { |
| sum += prob |
| } |
| if equalWithError(sum+truncation, 1.0, 0.0000001) { |
| t.Errorf("Incorrect PMF computed by getPrivacyLossDistribution: expected mass of the truncated distribution to be close to %f, got %f", 1.0-truncation, sum) |
| } |
| } |
| |
| func TestCorrectnessOfFindOptimalBitFlipProbability(t *testing.T) { |
| p1, err := findOptimalBitFlipProbability(1.0, -1, 64, 1, 0.01, 0.01) |
| if err != nil { |
| t.Errorf("findOptimalBitFlipProbability returned error: %s", err.Error()) |
| return |
| } |
| p2, err := findOptimalBitFlipProbability(1.0, -1, 64, 20, 0.01, 0.01) |
| if err != nil { |
| t.Errorf("findOptimalBitFlipProbability returned error: %s", err.Error()) |
| return |
| } |
| if p1 == p2 { |
| t.Errorf("Incorrectly computed optimal bit-flip probability for two different sparsity parameters: expected distinct values, got the same value") |
| } |
| } |
| |
| func TestFindOptimalBitFlipProbability(t *testing.T) { |
| p, err := findOptimalBitFlipProbability(1.0, 0.01, 50, 3, 0.001, 0.00001) |
| if err != nil { |
| t.Errorf("findOptimalBitFlipProbability returned error: %s", err.Error()) |
| return |
| } |
| if !equalWithError(p, 0.1989, 0.001) { |
| t.Errorf("Incorrectly computed optimal bit-flip probability: expected %f, got %f", 0.1989, p) |
| } |
| } |
| |
| func TestSortednessOfCheckPrivacyResults(t *testing.T) { |
| wasTrue := false |
| for p := 0.0; p <= 0.5; p += 0.09 { |
| isPrivate, err := checkPrivacy(1.0, 15, 3, 0.01, p, 0.01) |
| if err != nil { |
| t.Errorf("checkPrivacy returned error: %s", err.Error()) |
| return |
| } |
| if isPrivate { |
| wasTrue = true |
| } else { |
| if wasTrue { |
| t.Errorf("For bit-flip-probability %f we got that the algorithm is not DP, though for the previous value of bit-flip-probability %f the algorithm was private", p, p-0.01) |
| return |
| } |
| } |
| } |
| } |
| |
| func TestGetDeltaForPrivacyLossDistributionDecreasingValues(t *testing.T) { |
| prevDivergence := math.Inf(+1) |
| for p := 0.01; p <= 0.5; p += 0.09 { |
| pld, err := getPrivacyLossDistribution(5, 3, p, 0.001*0.01, 0.01) |
| if err != nil { |
| t.Errorf("getPrivacyLossDistribution on p=%f returned error: %s", p, err.Error()) |
| return |
| } |
| divergence := pld.getDeltaForPrivacyLossDistribution(1.0) |
| if divergence > prevDivergence { |
| t.Errorf("Divergence should decrease, but it increased with growing p from: %f to %f:", prevDivergence, divergence) |
| } |
| } |
| } |
| |
| func TestRootMeanSquaredError(t *testing.T) { |
| rmse := rootMeanSquaredError(0.4, 30, 6) |
| if !equalWithError(127.9843, rmse, 0.001) { |
| t.Errorf("Incorrect result of rootMeanSquaredError: expected error to be %f got %f", 127.9843, rmse) |
| } |
| rmse = rootMeanSquaredError(0.3, 100, 4) |
| if !equalWithError(62.7495, rmse, 0.001) { |
| t.Errorf("Incorrect result of rootMeanSquaredError: expected error to be %f got %f", 62.7495, rmse) |
| } |
| } |
| |
| func TestRealSumRMSE(t *testing.T) { |
| rmse := realSumRMSE(0.4, 35, 5) |
| if !equalWithError(21.5023, rmse, 0.001) { |
| t.Errorf("Incorrect result of realSumRMSE: expected error to be %f got %f", 21.5023, rmse) |
| } |
| rmse = realSumRMSE(0.2, 150, 9) |
| if !equalWithError(15.3307, rmse, 0.001) { |
| t.Errorf("Incorrect result of realSumRMSE: expected error to be %f got %f", 15.3307, rmse) |
| } |
| } |
| |
| func TestPyramidNumber(t *testing.T) { |
| number := pyramidNumber(5.0) |
| if !equalWithError(55.0, number, 0.001) { |
| t.Errorf("Incorrect result of pyramidNumber: expected to get %f got %f", 55.0, number) |
| } |
| number = pyramidNumber(0.0) |
| if !equalWithError(0.0, number, 0.001) { |
| t.Errorf("Incorrect result of pyramidNumber: expected to get %f got %f", 0.0, number) |
| } |
| } |
| |
| func TestFindOptimalDiscretization(t *testing.T) { |
| discretization := findOptimalDiscretization(0.1, 1000) |
| if 2 != discretization { |
| t.Errorf("Incorrect result of findOptimalDiscretization: expected discretization to be %d got %d", 2, discretization) |
| } |
| discretization = findOptimalDiscretization(0.01, 1000) |
| if 5 != discretization { |
| t.Errorf("Incorrect result of findOptimalDiscretization: expected discretization to be %d got %d", 5, discretization) |
| } |
| } |
| |
| func TestFindPrivacyEncodingParameters(t *testing.T) { |
| p, discretization, err := FindPrivacyEncodingParameters(1.0, 50, 5) |
| if err != nil { |
| t.Errorf("FindPrivacyEncodingParameters returned error: %s", err.Error()) |
| return |
| } |
| if !equalWithError(p, 0.223, 0.001) { |
| t.Errorf("Incorrectly computed optimal bit-flip probability: expected %f, got %f", 0.223, p) |
| } |
| if discretization != 2 { |
| t.Errorf("Incorrectly computed optimal discretization: expected %d, got %d", 2, discretization) |
| } |
| } |