Skip to content

Commit

Permalink
Add NONE DirectNoiseMechanism. (#1040)
Browse files Browse the repository at this point in the history
* Add NONE directNoiseMechanism for testing purpose
* Change direct measurement noise mechanism name to
`directNoiseMechanism` to differentiate from MPC `noiseMechanism`
* Some improvements to GaussianNoiser
  • Loading branch information
iverson52000 authored Jun 12, 2023
1 parent 73d4526 commit 615242e
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ import org.wfanet.measurement.eventdataprovider.privacybudgetmanagement.InMemory
import org.wfanet.measurement.eventdataprovider.privacybudgetmanagement.PrivacyBucketFilter
import org.wfanet.measurement.eventdataprovider.privacybudgetmanagement.PrivacyBudgetManager
import org.wfanet.measurement.eventdataprovider.privacybudgetmanagement.testing.TestPrivacyBucketMapper
import org.wfanet.measurement.loadtest.dataprovider.DirectNoiseMechanism
import org.wfanet.measurement.loadtest.dataprovider.EdpData
import org.wfanet.measurement.loadtest.dataprovider.EdpSimulator
import org.wfanet.measurement.loadtest.dataprovider.NoiseMechanism
import org.wfanet.measurement.loadtest.dataprovider.RandomEventQuery
import org.wfanet.measurement.loadtest.dataprovider.SketchGenerationParams
import org.wfanet.measurement.loadtest.storage.SketchStore
Expand Down Expand Up @@ -102,7 +102,7 @@ class InProcessEdpSimulator(
),
trustedCertificates = trustedCertificates,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)

private lateinit var edpJob: Job
Expand Down Expand Up @@ -130,6 +130,6 @@ class InProcessEdpSimulator(
private val logger: Logger = Logger.getLogger(this::class.java.name)
private const val RANDOM_SEED: Long = 1
private val random = Random(RANDOM_SEED)
private val NOISE_MECHANISM = NoiseMechanism.LAPLACE
private val DIRECT_NOISE_MECHANISM = DirectNoiseMechanism.LAPLACE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import org.apache.commons.math3.distribution.ConstantRealDistribution
import org.wfanet.anysketch.AnySketch
import org.wfanet.anysketch.Sketch
import org.wfanet.anysketch.SketchConfig
Expand Down Expand Up @@ -139,12 +140,14 @@ data class EdpData(
)

/**
* Mechanism for generating noise for direct measurements.
* Noise mechanism for generating publisher noise for direct measurements.
*
* TODO(@iverson52000): Move this to public API if EDP needs to report back the noise mechanism for
* PBM tracking.
* TODO(@iverson52000): Move this to public API if EDP needs to report back the direct noise
* mechanism for PBM tracking. NONE mechanism is testing only and should not move to public API.
*/
enum class NoiseMechanism {
enum class DirectNoiseMechanism {
/** NONE mechanism is testing only. */
NONE,
LAPLACE,
GAUSSIAN,
}
Expand All @@ -166,7 +169,7 @@ class EdpSimulator(
private val privacyBudgetManager: PrivacyBudgetManager,
private val trustedCertificates: Map<ByteString, X509Certificate>,
private val random: Random,
private val noiseMechanism: NoiseMechanism
private val directNoiseMechanism: DirectNoiseMechanism
) {

/** A sequence of operations done in the simulator. */
Expand Down Expand Up @@ -748,6 +751,20 @@ class EdpSimulator(
fulfillDirectMeasurement(requisition, requisitionSpec, measurementSpec, measurementResult)
}

private fun getPublisherNoiser(
privacyParams: DifferentialPrivacyParams,
directNoiseMechanism: DirectNoiseMechanism,
random: Random
): AbstractNoiser =
when (directNoiseMechanism) {
DirectNoiseMechanism.NONE ->
object : AbstractNoiser() {
override val distribution = ConstantRealDistribution(0.0)
}
DirectNoiseMechanism.LAPLACE -> LaplaceNoiser(privacyParams, random)
DirectNoiseMechanism.GAUSSIAN -> GaussianNoiser(privacyParams, random)
}

/**
* Add publisher noise to calculated direct reach.
*
Expand All @@ -760,10 +777,7 @@ class EdpSimulator(
privacyParams: DifferentialPrivacyParams
): Long {
val reachNoiser: AbstractNoiser =
when (noiseMechanism) {
NoiseMechanism.LAPLACE -> LaplaceNoiser(privacyParams, random)
NoiseMechanism.GAUSSIAN -> GaussianNoiser(privacyParams, random)
}
getPublisherNoiser(privacyParams, directNoiseMechanism, random)

return reachValue + reachNoiser.sample().toInt()
}
Expand All @@ -782,10 +796,7 @@ class EdpSimulator(
privacyParams: DifferentialPrivacyParams,
): Map<Long, Double> {
val frequencyNoiser: AbstractNoiser =
when (noiseMechanism) {
NoiseMechanism.LAPLACE -> LaplaceNoiser(privacyParams, random)
NoiseMechanism.GAUSSIAN -> GaussianNoiser(privacyParams, random)
}
getPublisherNoiser(privacyParams, directNoiseMechanism, random)

return frequencyMap.mapValues { (_, percentage) ->
(percentage * reachValue.toDouble() + frequencyNoiser.sample()) / reachValue.toDouble()
Expand All @@ -809,7 +820,7 @@ class EdpSimulator(
val sampledReachValue = calculateDirectReach(vidList)
val frequencyMap = calculateDirectFrequency(vidList, sampledReachValue)

logger.info("Adding $noiseMechanism publisher noise to direct reach and frequency...")
logger.info("Adding $directNoiseMechanism publisher noise to direct reach and frequency...")
val sampledNoisedReachValue =
addReachPublisherNoise(
sampledReachValue,
Expand All @@ -836,7 +847,7 @@ class EdpSimulator(
}
MeasurementSpec.MeasurementTypeCase.REACH -> {
val sampledReachValue = calculateDirectReach(vidList)
logger.info("Adding $noiseMechanism publisher noise to direct reach...")
logger.info("Adding $directNoiseMechanism publisher noise to direct reach...")
val sampledNoisedReachValue =
addReachPublisherNoise(sampledReachValue, measurementSpec.reach.privacyParams)
val scaledNoisedReachValue =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ class EdpSimulatorFlags {
private set

@CommandLine.Option(
names = ["--noise-mechanism"],
names = ["--direct-noise-mechanism"],
description = ["Differential privacy noise mechanism for direct measurements"],
defaultValue = "LAPLACE",
)
lateinit var noiseMechanism: NoiseMechanism
lateinit var directNoiseMechanism: DirectNoiseMechanism
private set
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ abstract class EdpSimulatorRunner : Runnable {
createNoOpPrivacyBudgetManager(),
clientCerts.trustedCertificates,
random,
flags.noiseMechanism
flags.directNoiseMechanism
)
runBlocking {
edpSimulator.createEventGroup()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ class GaussianNoiser(privacyParams: DifferentialPrivacyParams, random: Random) :
* N(0,1)
*/
private fun solveDelta(epsilon: Double, sigma: Double): Double {
val normalDistribution = NormalDistribution(0.0, 1.0)
val x = sigma * epsilon + 1 / (2 * sigma)

return (1 - normalDistribution.cumulativeProbability(x - 1 / sigma)) -
exp(epsilon) * (1 - normalDistribution.cumulativeProbability(x))
return (1 - standardNormalDistribution.cumulativeProbability(x - 1 / sigma)) -
exp(epsilon) * (1 - standardNormalDistribution.cumulativeProbability(x))
}
/**
* Assuming sensitivity = 1, solve std given epsilon and delta.
Expand All @@ -51,15 +50,17 @@ class GaussianNoiser(privacyParams: DifferentialPrivacyParams, random: Random) :
* find an upper bound of sigma and then apply the bisection search.
*/
private fun solveSigma(epsilon: Double, delta: Double, startingSigma: Double = 1e-3): Double {
var sigma = startingSigma
require(solveDelta(epsilon, sigma) >= delta) { "startingSigma $startingSigma is too large" }
require(solveDelta(epsilon, startingSigma) >= delta) {
"startingSigma $startingSigma is too large"
}

var sigma = startingSigma
while (solveDelta(epsilon, sigma) > delta) {
sigma *= 2
}

return BisectionSolver()
.solve(10000, { x: Double -> solveDelta(epsilon, x) - delta }, sigma / 2, sigma)
.solve(MAX_EVAL, { x: Double -> solveDelta(epsilon, x) - delta }, sigma / 2, sigma)
}
private fun getNormalDistribution(
epsilon: Double,
Expand All @@ -70,4 +71,9 @@ class GaussianNoiser(privacyParams: DifferentialPrivacyParams, random: Random) :

return NormalDistribution(RandomGeneratorFactory.createRandomGenerator(random), 0.0, sigma)
}

companion object {
private val standardNormalDistribution = NormalDistribution(0.0, 1.0)
private const val MAX_EVAL = 10000
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ private val REQUISITION_ONE_SPEC = requisitionSpec {
}
private const val DUCHY_ID = "worker1"
private const val RANDOM_SEED: Long = 1
private val NOISE_MECHANISM = NoiseMechanism.LAPLACE
private val DIRECT_NOISE_MECHANISM = DirectNoiseMechanism.LAPLACE

@RunWith(JUnit4::class)
class EdpSimulatorTest {
Expand Down Expand Up @@ -411,7 +411,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)
edpSimulator.createEventGroup()
edpSimulator.executeRequisitionFulfillingWorkflow()
Expand Down Expand Up @@ -504,7 +504,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)
val requisition =
REQUISITION_ONE.copy {
Expand Down Expand Up @@ -568,7 +568,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)

val vidSamplingIntervalWidth = 1f
Expand Down Expand Up @@ -643,7 +643,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)

val vidSamplingIntervalWidth = 1f
Expand Down Expand Up @@ -712,7 +712,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)

val vidSamplingIntervalWidth = 0.1f
Expand Down Expand Up @@ -787,7 +787,7 @@ class EdpSimulatorTest {
privacyBudgetManager,
TRUSTED_CERTIFICATES,
random,
NOISE_MECHANISM
DIRECT_NOISE_MECHANISM
)

val vidSamplingIntervalWidth = 0.1f
Expand Down

0 comments on commit 615242e

Please sign in to comment.