| // Copyright 2023 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" |
| |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| func cmpFloat() cmp.Option { |
| return cmpFloatDelta(1e-10) |
| } |
| |
| func cmpFloatDelta(precision float64) cmp.Option { |
| return cmp.Comparer(func(x, y float64) bool { |
| return math.Abs(x-y) < precision |
| }) |
| } |
| |
| func TestPoissonDist(t *testing.T) { |
| if d, err := newPoissonDist(-1.0); d != nil || err == nil { |
| t.Error("Created a poisson distribution with negative parameter.") |
| } |
| |
| lambda := 10.0 |
| dist, err := newPoissonDist(lambda) |
| if err != nil { |
| t.Fatalf("Could not create poisson distribution: %v", err) |
| } |
| |
| length := 20 |
| goldCdf := make([]float64, length) |
| goldPmf := make([]float64, length) |
| gotCdf := make([]float64, length) |
| gotPmf := make([]float64, length) |
| |
| for k := int(0); k < length; k++ { |
| gotCdf[k] = dist.cdf(float64(k)) |
| gotPmf[k] = dist.pmf(float64(k)) |
| |
| if k == 0 { |
| goldPmf[0] = math.Exp(-lambda) |
| goldCdf[0] = goldPmf[0] |
| } else { |
| goldPmf[k] = goldPmf[k-1] * lambda / float64(k) |
| goldCdf[k] = goldCdf[k-1] + goldPmf[k] |
| } |
| |
| } |
| |
| if s := cmp.Diff(goldPmf, gotPmf, cmpFloat()); len(s) > 0 { |
| t.Errorf("goldPmf != gotPmf:\n%s", s) |
| } |
| |
| if s := cmp.Diff(goldCdf, gotCdf, cmpFloat()); len(s) > 0 { |
| t.Errorf("goldCdf != gotCdf:\n%s", s) |
| } |
| } |
| |
| func TestFindBoundary(t *testing.T) { |
| expected := 5.0 |
| fn := func(x float64) bool { return x <= 5.5 } |
| |
| if got := findBoundary(fn); got != expected { |
| t.Errorf("Expected findBoundary(fn) = %v but got %v.", expected, got) |
| } |
| } |
| |
| func getBoundaryFn(boundary float64) func(float64) bool { |
| return func(x float64) bool { return x < boundary } |
| } |
| |
| func TestFindBoundaryFloat(t *testing.T) { |
| low := 0.0 |
| high := 10.0 |
| precision := 0.01 |
| |
| expected := 3.0 |
| |
| x := findBoundaryFloat(getBoundaryFn(expected), low, high, precision) |
| if !cmp.Equal(x, expected, cmpFloatDelta(precision)) { |
| t.Errorf("findBoundaryError expected %v but got %v", expected, x) |
| } |
| |
| expected = 20.0 |
| x = findBoundaryFloat(getBoundaryFn(expected), low, high, precision) |
| if !cmp.Equal(x, expected, cmpFloatDelta(precision)) { |
| t.Errorf("findBoundaryError expected %v but got %v", expected, x) |
| } |
| } |
| |
| func TestPldAddOne(t *testing.T) { |
| discretization := 0.01 |
| truncation := 0.01 |
| lambda := 10.0 |
| res := newPoissonAddPld(lambda, discretization, truncation) |
| |
| if !cmp.Equal(res.discretization, discretization, cmpFloat()) { |
| t.Errorf("PoissonAddPld() discretization got %f, expected %f", res.discretization, discretization) |
| } |
| |
| goldInfiniteMass := 0.0027693957155115775 |
| if !cmp.Equal(res.infiniteMass, goldInfiniteMass, cmpFloat()) { |
| t.Errorf("PoissonAddPld() infiniteMass got %f, expected %f", res.infiniteMass, goldInfiniteMass) |
| } |
| |
| if !res.pessimisticEstimation { |
| t.Errorf("PoissonAddPld() pessimisticEstimation got false") |
| } |
| |
| expectedPmf := make(ProbabilityMassFunction) |
| expectedPmf[-64] = 0.007186504603854357 |
| expectedPmf[-58] = 0.007091108993195334 |
| expectedPmf[-53] = 0.012763996187751505 |
| expectedPmf[-47] = 0.021698793519177594 |
| expectedPmf[-40] = 0.034718069630684245 |
| expectedPmf[-33] = 0.05207710444602615 |
| expectedPmf[-26] = 0.07290794622443707 |
| expectedPmf[-18] = 0.09478033009176803 |
| expectedPmf[-9] = 0.11373639611012128 |
| expectedPmf[0] = 0.12511003572113372 |
| expectedPmf[11] = 0.12511003572113372 |
| expectedPmf[23] = 0.11259903214902009 |
| expectedPmf[36] = 0.090079225719216 |
| expectedPmf[52] = 0.06305545800345125 |
| expectedPmf[70] = 0.03783327480207079 |
| expectedPmf[92] = 0.01891663740103538 |
| expectedPmf[121] = 0.007566654960414144 |
| |
| if diff := cmp.Diff(expectedPmf, res.pmf, cmpFloat()); diff != "" { |
| t.Errorf("%v.PoissonAddPld() pmf diff (-want +got):\n%s", res.pmf, diff) |
| } |
| } |
| |
| func TestPldRemoveOne(t *testing.T) { |
| discretization := 0.01 |
| truncation := 0.01 |
| lambda := 10.0 |
| res := newPoissonRemovePld(lambda, discretization, truncation) |
| |
| if !cmp.Equal(res.discretization, discretization, cmpFloat()) { |
| t.Errorf("PoissonRemovePld() discretization got %f, expected %f", res.discretization, discretization) |
| } |
| |
| goldInfiniteMass := 0.0034543419758568334 |
| if !cmp.Equal(res.infiniteMass, goldInfiniteMass, cmpFloat()) { |
| t.Errorf("PoissonRemovePld() infiniteMass got %f, expected %f", res.infiniteMass, goldInfiniteMass) |
| } |
| |
| if !res.pessimisticEstimation { |
| t.Errorf("PoissonRemovePld() pessimisticEstimation got false") |
| } |
| |
| expectedPmf := make(ProbabilityMassFunction) |
| expectedPmf[-91] = 0.010336050675925721 |
| expectedPmf[-69] = 0.01891663740103538 |
| expectedPmf[-51] = 0.03783327480207079 |
| expectedPmf[-35] = 0.06305545800345125 |
| expectedPmf[-22] = 0.090079225719216 |
| expectedPmf[-10] = 0.11259903214902009 |
| expectedPmf[0] = 0.12511003572113372 |
| expectedPmf[10] = 0.12511003572113372 |
| expectedPmf[19] = 0.11373639611012128 |
| expectedPmf[27] = 0.09478033009176803 |
| expectedPmf[34] = 0.07290794622443707 |
| expectedPmf[41] = 0.05207710444602615 |
| expectedPmf[48] = 0.034718069630684245 |
| expectedPmf[54] = 0.021698793519177594 |
| expectedPmf[59] = 0.012763996187751505 |
| expectedPmf[65] = 0.007091108993195334 |
| expectedPmf[70] = 0.003732162627997529 |
| |
| if diff := cmp.Diff(expectedPmf, res.pmf, cmpFloat()); diff != "" { |
| t.Errorf("%v.PoissonRemovePld() pmf diff (-want +got):\n%s", res.pmf, diff) |
| } |
| } |
| |
| func TestIsPrivate(t *testing.T) { |
| c := NewPoissonParameterCalculator() |
| |
| lambda := 85.937 |
| epsilon := 1.0 |
| delta := 1e-10 |
| sparsity := uint64(1) |
| |
| if !c.IsPrivate(lambda, sparsity, epsilon, delta) { |
| t.Errorf("IsPrivate unexpectedly false") |
| } |
| |
| lambda = lambda - 10 |
| if c.IsPrivate(lambda, sparsity, epsilon, delta) { |
| t.Errorf("IsPrivate unexpectedly true") |
| } |
| } |