blob: 5b09f88c128dd18eae8df2fa2d908b7bed021db7 [file] [log] [blame]
// 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")
}
}