diff --git a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Covariances.kt b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Covariances.kt index 086036ab185..358ef85b2d5 100644 --- a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Covariances.kt +++ b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Covariances.kt @@ -34,10 +34,16 @@ object Covariances { val overlapReach = reachMeasurementCovarianceParams.reach + reachMeasurementCovarianceParams.otherReach - reachMeasurementCovarianceParams.unionReach + val overlapSamplingWidth = reachMeasurementCovarianceParams.samplingWidth + reachMeasurementCovarianceParams.otherSamplingWidth - reachMeasurementCovarianceParams.unionSamplingWidth + require(overlapSamplingWidth >= 0.0 && overlapSamplingWidth <= 1.0) { + "Overlap sampling width must be greater than or equal to 0 and less than or equal to 1, but" + + " got $overlapSamplingWidth." + } + return overlapReach * (overlapSamplingWidth / reachMeasurementCovarianceParams.samplingWidth / @@ -55,6 +61,7 @@ object Covariances { sketchParams: LiquidLegionsSketchParams, reachMeasurementCovarianceParams: ReachMeasurementCovarianceParams, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) return LiquidLegions.inflatedReachCovariance( sketchParams = sketchParams, reach = reachMeasurementCovarianceParams.reach, @@ -89,6 +96,10 @@ object Covariances { otherWeightedMeasurementVarianceParams.measurementVarianceParams.measurementParams .vidSamplingInterval, ) + require(unionSamplingWidth >= 0.0 && unionSamplingWidth <= 1.0) { + "The union sampling width must be greater than or equal to 0 and less than or equal to 1, " + + "but got $unionSamplingWidth." + } val liquidLegionsSketchParams = when (val methodology = weightedMeasurementVarianceParams.methodology) { diff --git a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/HonestMajorityShareShuffle.kt b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/HonestMajorityShareShuffle.kt index 814e384e789..2cf1f67dfae 100644 --- a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/HonestMajorityShareShuffle.kt +++ b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/HonestMajorityShareShuffle.kt @@ -30,15 +30,21 @@ object HonestMajorityShareShuffle { reachNoiseVariance: Double, ): Double { val vidUniverseSize: Long = ceil(frequencyVectorSize / vidSamplingIntervalWidth).toLong() - require(vidUniverseSize > 1) { "Vid universe size is too small." } + require(vidUniverseSize > 1) { + "Vid universe size must be greater than 1, but got $vidUniverseSize." + } require(vidSamplingIntervalWidth > 0.0 && vidSamplingIntervalWidth <= 1.0) { - "Vid sampling width must be greater than 0 and less than or equal to 1." + "Vid sampling width must be greater than 0 and less than or equal to 1, but got " + + "$vidSamplingIntervalWidth." } require(reach <= vidUniverseSize) { - "Reach must be less than or equal to the size of the Vid universe." + "Reach ($reach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." + } + require(reachNoiseVariance >= 0) { + "Reach noise variance must be a non-negative value, but got $reachNoiseVariance." } - require(reachNoiseVariance >= 0) { "Reach noise variance must be a non-negative value." } val reachVariance = (vidSamplingIntervalWidth * @@ -66,14 +72,18 @@ object HonestMajorityShareShuffle { val vidSamplingIntervalWidth = frequencyMeasurementParams.vidSamplingInterval.width val vidUniverseSize = ceil(frequencyVectorSize / vidSamplingIntervalWidth).toLong() - require(vidUniverseSize > 1) { "Vid universe size is too small." } + require(vidUniverseSize > 1) { + "Vid universe size must be greater than 1, but got $vidUniverseSize." + } require(totalReach <= vidUniverseSize) { - "Reach must be less than or equal to the size of the Vid universe." + "Total reach ($totalReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } val kReach = (kReachRatio * totalReach).toLong() require(kReach <= vidUniverseSize) { - "kReach must be less than or equal to the size of the Vid universe." + "kReach ($kReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } var kReachVariance = @@ -113,14 +123,18 @@ object HonestMajorityShareShuffle { val vidSamplingIntervalWidth = frequencyMeasurementParams.vidSamplingInterval.width val vidUniverseSize = ceil(frequencyVectorSize / vidSamplingIntervalWidth).toLong() - require(vidUniverseSize > 1) { "Vid universe size is too small." } + require(vidUniverseSize > 1) { + "Vid universe size must be greater than 1, but got $vidUniverseSize." + } require(totalReach <= vidUniverseSize) { - "Reach must be less than or equal to the size of the Vid universe." + "Total reach ($totalReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } val kPlusReach = (kReachRatio * totalReach).toLong() require(kPlusReach <= vidUniverseSize) { - "kPlusReach must be less than or equal to the size of the Vid universe." + "kPlusReach ($kPlusReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } // Gets the reach noise variance from the reach measurement variance and total reach. @@ -167,14 +181,18 @@ object HonestMajorityShareShuffle { val vidSamplingIntervalWidth = frequencyMeasurementParams.vidSamplingInterval.width val vidUniverseSize = ceil(frequencyVectorSize / vidSamplingIntervalWidth).toLong() - require(vidUniverseSize > 1) { "Vid universe size is too small." } + require(vidUniverseSize > 1) { + "Vid universe size must be greater than 1, but got $vidUniverseSize." + } require(totalReach <= vidUniverseSize) { - "Reach must be less than or equal to the size of the Vid universe." + "Total reach ($totalReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } val kReach = (kReachRatio * totalReach).toLong() require(kReach <= vidUniverseSize) { - "kReach must be less than or equal to the size of the Vid universe." + "kReach ($kReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } var kReachVariance = @@ -235,14 +253,18 @@ object HonestMajorityShareShuffle { val vidSamplingIntervalWidth = frequencyMeasurementParams.vidSamplingInterval.width val vidUniverseSize = ceil(frequencyVectorSize / vidSamplingIntervalWidth).toLong() - require(vidUniverseSize > 1) { "Vid universe size is too small." } + require(vidUniverseSize > 1) { + "Vid universe size must be greater than 1, but got $vidUniverseSize." + } require(totalReach <= vidUniverseSize) { - "Reach must be less than or equal to the size of the Vid universe." + "Total reach ($totalReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } val kPlusReach = (kPlusReachRatio * totalReach).toLong() require(kPlusReach <= vidUniverseSize) { - "kPlusReach must be less than or equal to the size of the Vid universe." + "kPlusReach ($kPlusReach) must be less than or equal to the size of the Vid universe " + + "($vidUniverseSize)." } // Gets the reach noise variance from the reach measurement variance and total reach. diff --git a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/LiquidLegions.kt b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/LiquidLegions.kt index c65f32cc9f6..9c2e28717d0 100644 --- a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/LiquidLegions.kt +++ b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/LiquidLegions.kt @@ -39,6 +39,16 @@ data class LiquidLegionsSketchParams(val decayRate: Double, val sketchSize: Long } } +/** Verifies that the liquid legions sketch params are valid. */ +fun verifyLiquidLegionsSketchParams(sketchParams: LiquidLegionsSketchParams) { + require(sketchParams.sketchSize > 0) { + "Sketch size must be positive, but got ${sketchParams.sketchSize}." + } + require(sketchParams.decayRate > 0) { + "Decay rate must be positive, but got ${sketchParams.decayRate}." + } +} + /** Functions to compute statistics of Liquid Legions sketch based measurements. */ object LiquidLegions { /** Exponential integral function for negative inputs. */ @@ -90,6 +100,7 @@ object LiquidLegions { y: Double, liquidLegionsSketchParams: LiquidLegionsSketchParams, ): Double { + verifyLiquidLegionsSketchParams(liquidLegionsSketchParams) if (k < 0.0) { throw IllegalArgumentException("Invalid inputs: k=$k < 0.") } @@ -128,6 +139,21 @@ object LiquidLegions { overlapSamplingWidth: Double, inflation: Double = 0.0, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) + require(reach > 0) { "Reach must be positive, but got $reach." } + require(otherReach > 0) { "Other reach must be positive, but got $otherReach." } + require(samplingWidth > 0.0 && samplingWidth <= 1.0) { + "Sampling width must be greater than 0 and less than or equal to 1, but got $samplingWidth." + } + require(otherSamplingWidth > 0.0 && otherSamplingWidth <= 1.0) { + "Other sampling width must be greater than 0 and less than or equal to 1, but got " + + "$otherSamplingWidth." + } + require(overlapSamplingWidth >= 0.0 && overlapSamplingWidth <= 1.0) { + "Other sampling width must be greater than or equal to 0 and less than or equal to 1, but " + + "got $overlapSamplingWidth." + } + val y1 = max(1.0, reach * samplingWidth) val y2 = max(1.0, otherReach * otherSamplingWidth) val y12 = max(1.0, overlapReach * overlapSamplingWidth) @@ -160,6 +186,12 @@ object LiquidLegions { totalReach: Long, vidSamplingIntervalWidth: Double, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) + require(totalReach > 0) { "Total reach must be positive, but got $totalReach." } + require(vidSamplingIntervalWidth > 0.0 && vidSamplingIntervalWidth <= 1.0) { + "Vid sampling width must be greater than 0 and less than or equal to 1, but got " + + "$vidSamplingIntervalWidth." + } // Expected sampled reach val expectedReach = totalReach * vidSamplingIntervalWidth if (expectedReach < 2.0) { @@ -179,6 +211,12 @@ object LiquidLegions { totalReach: Long, vidSamplingIntervalWidth: Double, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) + require(totalReach > 0) { "Total reach must be positive, but got $totalReach" } + require(vidSamplingIntervalWidth > 0.0 && vidSamplingIntervalWidth <= 1.0) { + "Vid sampling width must be greater than 0 and less than or equal to 1, but got " + + "$vidSamplingIntervalWidth." + } // Expected sampled reach val expectedReach = totalReach * vidSamplingIntervalWidth // The mathematical formulas below assume the sampled reach >= 3. If sampled reach < 3, the @@ -226,6 +264,16 @@ object LiquidLegions { reachRatio: Double, vidSamplingIntervalWidth: Double, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) + require(totalReach > 0) { "Total reach must be positive, but got $totalReach." } + require(reachRatio >= 0.0 && reachRatio <= 1.0) { + "Reach ratio must be greater than or equal to 0 and less than or equal to 1, but got " + + "$reachRatio." + } + require(vidSamplingIntervalWidth > 0.0 && vidSamplingIntervalWidth <= 1.0) { + "Vid sampling width must be greater than 0 and less than or equal to 1, but got " + + "$vidSamplingIntervalWidth." + } val tmp = reachRatio * (1.0 - reachRatio) / totalReach val expectedRegisterNum = expectedNumberOfNonDestroyedRegisters( @@ -255,6 +303,7 @@ object LiquidLegions { frequencyNoiseVariance: Double, relativeFrequencyMeasurementVarianceParams: RelativeFrequencyMeasurementVarianceParams, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) val ( totalReach: Long, reachMeasurementVariance: Double, diff --git a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Variances.kt b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Variances.kt index 7f06e32035e..291cc909625 100644 --- a/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Variances.kt +++ b/src/main/kotlin/org/wfanet/measurement/measurementconsumer/stats/Variances.kt @@ -157,6 +157,12 @@ object VariancesImpl : Variances { multiplier: Int) = relativeFrequencyMeasurementVarianceParams + require(totalReach > 0) { "Total reach must be positive, but got $totalReach." } + require(reachRatio >= 0.0 && reachRatio <= 1.0) { + "Reach ratio must be greater than or equal to 0 and less than or equal to 1, but got " + + "$reachRatio." + } + // When reach is too small, we have little info to estimate frequency, and thus the estimate of // relative frequency is equivalent to a uniformly random guess at probability. if ( @@ -189,6 +195,17 @@ object VariancesImpl : Variances { reachRatio: Double, reachRatioVariance: Double, ): Double { + require(totalReach > 0) { "Total reach must be positive, but got $totalReach." } + require(totalReachVariance >= 0) { + "Total reach variance must not be negative, but got $totalReachVariance." + } + require(reachRatio >= 0.0 && reachRatio <= 1.0) { + "Reach ratio must be greater than or equal to 0 and less than or equal to 1, but got " + + "$reachRatio." + } + require(reachRatioVariance >= 0) { + "Reach ratio variance must not be negative, but got $reachRatioVariance." + } val variance = reachRatioVariance * totalReachVariance + reachRatioVariance * totalReach.toDouble().pow(2) + @@ -206,7 +223,9 @@ object VariancesImpl : Variances { maximumFrequencyPerUser: Double, noiseMechanism: NoiseMechanism, ): Double { - require(measurementValue >= 0.0) { "The scalar measurement value cannot be negative." } + require(measurementValue >= 0.0) { + "The scalar measurement value ($measurementValue) cannot be negative." + } val noiseVariance: Double = computeDirectNoiseVariance(dpParams, noiseMechanism) val variance = (maximumFrequencyPerUser * @@ -226,6 +245,7 @@ object VariancesImpl : Variances { sketchParams: LiquidLegionsSketchParams, varianceParams: ReachMeasurementVarianceParams, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) val noiseVariance: Double = computeDirectNoiseVariance( varianceParams.measurementParams.dpParams, @@ -257,6 +277,7 @@ object VariancesImpl : Variances { sketchParams: LiquidLegionsSketchParams, params: FrequencyMeasurementVarianceParams, ): FrequencyVariances { + verifyLiquidLegionsSketchParams(sketchParams) return frequencyVariance( params, constructLiquidLegionsSketchFrequencyRelativeVariance(sketchParams, params.measurementParams), @@ -274,6 +295,7 @@ object VariancesImpl : Variances { ): ( relativeFrequencyMeasurementVarianceParams: RelativeFrequencyMeasurementVarianceParams ) -> Double { + verifyLiquidLegionsSketchParams(sketchParams) val frequencyNoiseVariance: Double = computeDirectNoiseVariance(measurementParams.dpParams, measurementParams.noiseMechanism) return { relativeFrequencyMeasurementVarianceParams -> @@ -291,11 +313,16 @@ object VariancesImpl : Variances { sketchParams: LiquidLegionsSketchParams, varianceParams: ReachMeasurementVarianceParams, ): Double { + verifyLiquidLegionsSketchParams(sketchParams) val distributedGaussianNoiseVariance: Double = computeDistributedNoiseVariance( varianceParams.measurementParams.dpParams, varianceParams.measurementParams.noiseMechanism, ) + require(distributedGaussianNoiseVariance >= 0.0) { + "Distributed Gaussian noise variance must not be negative, but got " + + "$distributedGaussianNoiseVariance." + } val variance = LiquidLegions.inflatedReachCovariance( @@ -320,6 +347,7 @@ object VariancesImpl : Variances { sketchParams: LiquidLegionsSketchParams, params: FrequencyMeasurementVarianceParams, ): FrequencyVariances { + verifyLiquidLegionsSketchParams(sketchParams) return frequencyVariance( params, constructLiquidLegionsV2FrequencyRelativeVariance(sketchParams, params.measurementParams), @@ -337,9 +365,12 @@ object VariancesImpl : Variances { ): ( relativeFrequencyMeasurementVarianceParams: RelativeFrequencyMeasurementVarianceParams ) -> Double { + verifyLiquidLegionsSketchParams(sketchParams) val frequencyNoiseVariance: Double = computeDistributedNoiseVariance(measurementParams.dpParams, measurementParams.noiseMechanism) - + require(frequencyNoiseVariance >= 0.0) { + "Frequency noise variance must not be negative, but got $frequencyNoiseVariance." + } return { relativeFrequencyMeasurementVarianceParams -> LiquidLegions.liquidLegionsFrequencyRelativeVariance( sketchParams = sketchParams, @@ -400,9 +431,11 @@ object VariancesImpl : Variances { totalReach: Long, totalReachVariance: Double, reachRatio: Double, reachRatioVariance: Double, ) -> Double, ): FrequencyVariances { - require(params.totalReach >= 0.0) { "The total reach value cannot be negative." } + require(params.totalReach >= 0.0) { + "The total reach value (${params.totalReach}) cannot be negative." + } require(params.reachMeasurementVariance >= 0.0) { - "The reach variance value cannot be negative." + "The reach variance value (${params.reachMeasurementVariance}) cannot be negative." } val maximumFrequency = params.measurementParams.maximumFrequency @@ -501,9 +534,11 @@ object VariancesImpl : Variances { frequencyVectorSize: Long, frequencyParams: FrequencyMeasurementVarianceParams, ): FrequencyVariances { - require(frequencyParams.totalReach >= 0.0) { "The total reach value cannot be negative." } + require(frequencyParams.totalReach >= 0.0) { + "The total reach value (${frequencyParams.totalReach}) cannot be negative." + } require(frequencyParams.reachMeasurementVariance >= 0.0) { - "The reach variance value cannot be negative." + "The reach variance value (${frequencyParams.reachMeasurementVariance}) cannot be negative." } val maximumFrequency = frequencyParams.measurementParams.maximumFrequency @@ -607,9 +642,11 @@ object VariancesImpl : Variances { totalReach: Long, totalReachVariance: Double, reachRatio: Double, reachRatioVariance: Double, ) -> Double, ): FrequencyVariances { - require(params.totalReach >= 0.0) { "The total reach value cannot be negative." } + require(params.totalReach >= 0.0) { + "The total reach value (${params.totalReach}) cannot be negative." + } require(params.reachMeasurementVariance >= 0.0) { - "The reach variance value cannot be negative." + "The reach variance value (${params.reachMeasurementVariance}) cannot be negative." } val maximumFrequency = params.measurementParams.maximumFrequency @@ -752,7 +789,8 @@ object VariancesImpl : Variances { require(params.weightedMeasurementVarianceParamsList.size == 1) { "Only support variance calculation of frequency metrics computed on union-only set " + - "expressions." + "expressions. Expected exactly 1 weighted measurement variance params, but got " + + "${params.weightedMeasurementVarianceParamsList.size}." } val weightedMeasurementVarianceParams = params.weightedMeasurementVarianceParamsList.first() @@ -844,7 +882,8 @@ object VariancesImpl : Variances { require(params.weightedMeasurementVarianceParamsList.size == 1) { "Only support variance calculation of impression metrics computed on union-only set " + - "expressions." + "expressions. Expected exactly 1 weighted measurement variance params, but got " + + "${params.weightedMeasurementVarianceParamsList.size}." } val weightedMeasurementVarianceParams = params.weightedMeasurementVarianceParamsList.first() @@ -907,7 +946,8 @@ object VariancesImpl : Variances { require(params.weightedMeasurementVarianceParamsList.size == 1) { "Only support variance calculation of watch duration metrics computed on union-only set " + - "expressions." + "expressions. Expected exactly 1 weighted measurement variance params, but got " + + "${params.weightedMeasurementVarianceParamsList.size}." } val weightedMeasurementVarianceParams = params.weightedMeasurementVarianceParamsList.first() diff --git a/src/test/kotlin/org/wfanet/measurement/measurementconsumer/stats/VariancesTest.kt b/src/test/kotlin/org/wfanet/measurement/measurementconsumer/stats/VariancesTest.kt index 3e79dbb2163..e7ef79b7aa8 100644 --- a/src/test/kotlin/org/wfanet/measurement/measurementconsumer/stats/VariancesTest.kt +++ b/src/test/kotlin/org/wfanet/measurement/measurementconsumer/stats/VariancesTest.kt @@ -1087,6 +1087,54 @@ class VariancesTest { } } + @Test + fun `computeMeasurementVariance for LiquidLegionsSketch throws InvalINVALID_ARGUMENT when sketch size is invalid`() { + val decayRate = 1e-3 + val sketchSize = 0L + val reach = 2L + val vidSamplingIntervalWidth = 0.1 + val dpParams = DpParams(0.1, 1e-9) + val reachMeasurementParams = + ReachMeasurementParams( + VidSamplingInterval(0.0, vidSamplingIntervalWidth), + dpParams, + NoiseMechanism.GAUSSIAN, + ) + val reachMeasurementVarianceParams = + ReachMeasurementVarianceParams(reach, reachMeasurementParams) + + assertFailsWith { + VariancesImpl.computeMeasurementVariance( + LiquidLegionsSketchMethodology(decayRate, sketchSize), + reachMeasurementVarianceParams, + ) + } + } + + @Test + fun `computeMeasurementVariance for LiquidLegionsSketch throws InvalINVALID_ARGUMENT when decay rate is invalid`() { + val decayRate = -5.0 + val sketchSize = 10L + val reach = 2L + val vidSamplingIntervalWidth = 0.1 + val dpParams = DpParams(0.1, 1e-9) + val reachMeasurementParams = + ReachMeasurementParams( + VidSamplingInterval(0.0, vidSamplingIntervalWidth), + dpParams, + NoiseMechanism.GAUSSIAN, + ) + val reachMeasurementVarianceParams = + ReachMeasurementVarianceParams(reach, reachMeasurementParams) + + assertFailsWith { + VariancesImpl.computeMeasurementVariance( + LiquidLegionsSketchMethodology(decayRate, sketchSize), + reachMeasurementVarianceParams, + ) + } + } + @Test fun `computeMeasurementVariance returns a value for LiquidLegionsSketch reach when reach is small, sampling width is small, and small decay rate`() { val decayRate = 1e-3 @@ -1301,6 +1349,54 @@ class VariancesTest { assertThat(variance).isWithin(tolerance).of(expected) } + @Test + fun `computeMeasurementVariance for LiquidLegionsV2 throws InvalINVALID_ARGUMENT when sketch size is invalid`() { + val decayRate = 1e-3 + val sketchSize = 0L + val reach = 2L + val vidSamplingIntervalWidth = 0.1 + val dpParams = DpParams(0.1, 1e-9) + val reachMeasurementParams = + ReachMeasurementParams( + VidSamplingInterval(0.0, vidSamplingIntervalWidth), + dpParams, + NoiseMechanism.GAUSSIAN, + ) + val reachMeasurementVarianceParams = + ReachMeasurementVarianceParams(reach, reachMeasurementParams) + + assertFailsWith { + VariancesImpl.computeMeasurementVariance( + LiquidLegionsV2Methodology(decayRate, sketchSize, 0L), + reachMeasurementVarianceParams, + ) + } + } + + @Test + fun `computeMeasurementVariance for LiquidLegionsV2 throws InvalINVALID_ARGUMENT when decay rate is invalid`() { + val decayRate = -5.0 + val sketchSize = 10L + val reach = 2L + val vidSamplingIntervalWidth = 0.1 + val dpParams = DpParams(0.1, 1e-9) + val reachMeasurementParams = + ReachMeasurementParams( + VidSamplingInterval(0.0, vidSamplingIntervalWidth), + dpParams, + NoiseMechanism.GAUSSIAN, + ) + val reachMeasurementVarianceParams = + ReachMeasurementVarianceParams(reach, reachMeasurementParams) + + assertFailsWith { + VariancesImpl.computeMeasurementVariance( + LiquidLegionsV2Methodology(decayRate, sketchSize, 0L), + reachMeasurementVarianceParams, + ) + } + } + @Test fun `computeMeasurementVariance returns a value for LiquidLegionsV2 reach when reach is small, sampling width is small, and small decay rate`() { val decayRate = 1e-3 @@ -4122,7 +4218,7 @@ class VariancesTest { noiseMechanism = NoiseMechanism.GAUSSIAN, ), ), - methodology = LiquidLegionsV2Methodology(0.0, 1e6.toLong(), 1e8.toLong()), + methodology = LiquidLegionsV2Methodology(0.001, 1e6.toLong(), 1e8.toLong()), ) val otherWeightedReachMeasurementVarianceParams = @@ -4170,7 +4266,7 @@ class VariancesTest { noiseMechanism = NoiseMechanism.GAUSSIAN, ), ), - methodology = LiquidLegionsV2Methodology(0.0, 1e6.toLong(), 1e8.toLong()), + methodology = LiquidLegionsV2Methodology(0.001, 1e6.toLong(), 1e8.toLong()), ) val otherWeightedReachMeasurementVarianceParams = @@ -4182,7 +4278,7 @@ class VariancesTest { reach = 1L, measurementParams = ReachMeasurementParams( - vidSamplingInterval = VidSamplingInterval(0.3, 0.8), + vidSamplingInterval = VidSamplingInterval(0.3, 0.7), dpParams = DpParams(0.1, 1e-9), noiseMechanism = NoiseMechanism.GAUSSIAN, ), @@ -4197,7 +4293,7 @@ class VariancesTest { val variance = VariancesImpl.computeMetricVariance(params) - val expected = 21253.74748438153 + val expected = 22459.91623264358 val tolerance = computeErrorTolerance(variance, expected) assertThat(variance).isWithin(tolerance).of(expected) } @@ -4218,7 +4314,7 @@ class VariancesTest { noiseMechanism = NoiseMechanism.GAUSSIAN, ), ), - methodology = LiquidLegionsV2Methodology(0.0, 1e6.toLong(), 1e8.toLong()), + methodology = LiquidLegionsV2Methodology(0.001, 1e6.toLong(), 1e8.toLong()), ) val otherWeightedReachMeasurementVarianceParams = @@ -4230,7 +4326,7 @@ class VariancesTest { reach = 1L, measurementParams = ReachMeasurementParams( - vidSamplingInterval = VidSamplingInterval(0.5, 1.0), + vidSamplingInterval = VidSamplingInterval(0.5, 0.5), dpParams = DpParams(0.1, 1e-9), noiseMechanism = NoiseMechanism.GAUSSIAN, ), @@ -4245,7 +4341,7 @@ class VariancesTest { val variance = VariancesImpl.computeMetricVariance(params) - val expected = 19836.523148381533 + val expected = 27400.78312697515 val tolerance = computeErrorTolerance(variance, expected) assertThat(variance).isWithin(tolerance).of(expected) } @@ -4266,7 +4362,7 @@ class VariancesTest { noiseMechanism = NoiseMechanism.GAUSSIAN, ), ), - methodology = LiquidLegionsV2Methodology(0.0, 1e6.toLong(), 1e8.toLong()), + methodology = LiquidLegionsV2Methodology(0.001, 1e6.toLong(), 1e8.toLong()), ) val varianceSingleMeasurement = @@ -4285,7 +4381,7 @@ class VariancesTest { reach = 1L, measurementParams = ReachMeasurementParams( - vidSamplingInterval = VidSamplingInterval(0.5, 1.0), + vidSamplingInterval = VidSamplingInterval(0.5, 0.5), dpParams = DpParams(0.1, 1e-9), noiseMechanism = NoiseMechanism.GAUSSIAN, ), @@ -4316,12 +4412,12 @@ class VariancesTest { reach = 4L, measurementParams = ReachMeasurementParams( - vidSamplingInterval = VidSamplingInterval(0.1, 1.0), + vidSamplingInterval = VidSamplingInterval(0.1, 0.9), dpParams = DpParams(0.1, 1e-9), noiseMechanism = NoiseMechanism.GAUSSIAN, ), ), - methodology = LiquidLegionsV2Methodology(0.0, 1e6.toLong(), 1e8.toLong()), + methodology = LiquidLegionsV2Methodology(0.001, 1e6.toLong(), 1e8.toLong()), ) val weightedReachMeasurementVarianceParams = @@ -4369,7 +4465,7 @@ class VariancesTest { val variance = VariancesImpl.computeMetricVariance(params) - val expected = 10554.13919363766 + val expected = 11568.523587919317 val tolerance = computeErrorTolerance(variance, expected) assertThat(variance).isWithin(tolerance).of(expected) }