Skip to content

Commit 48bda4f

Browse files
alexojicacopybara-github
authored andcommitted
Refactor contribution sampling to work with multiple features.
PiperOrigin-RevId: 811408440
1 parent c240793 commit 48bda4f

33 files changed

Lines changed: 2589 additions & 1101 deletions

pipelinedp4j/main/com/google/privacy/differentialprivacy/pipelinedp4j/api/AggregationSpec.kt

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -148,44 +148,6 @@ internal fun AggregationSpec.getFeatureId(): String {
148148
}
149149
}
150150

151-
internal fun List<AggregationSpec>.metrics(): List<MetricDefinition> = buildList {
152-
for (aggregation in this@metrics) {
153-
when (aggregation) {
154-
// Count and PrivacyIdCount do not aggregate any specific value, therefore they are handled
155-
// differently.
156-
is PrivacyIdCount ->
157-
add(
158-
MetricDefinition(
159-
MetricType.PRIVACY_ID_COUNT,
160-
aggregation.budget?.toInternalBudgetPerOpSpec(),
161-
)
162-
)
163-
is Count ->
164-
add(MetricDefinition(MetricType.COUNT, aggregation.budget?.toInternalBudgetPerOpSpec()))
165-
is ValueAggregations<*> -> {
166-
for (valueAggregationSpec in aggregation.valueAggregationSpecs) {
167-
add(
168-
MetricDefinition(
169-
valueAggregationSpec.metricType,
170-
valueAggregationSpec.budget?.toInternalBudgetPerOpSpec(),
171-
)
172-
)
173-
}
174-
}
175-
is VectorAggregations<*> -> {
176-
for (vectorAggregationSpec in aggregation.vectorAggregationSpecs) {
177-
add(
178-
MetricDefinition(
179-
vectorAggregationSpec.metricType,
180-
vectorAggregationSpec.budget?.toInternalBudgetPerOpSpec(),
181-
)
182-
)
183-
}
184-
}
185-
}
186-
}
187-
}
188-
189151
internal fun List<AggregationSpec>.outputColumnNamesWithMetricTypes():
190152
List<Pair<String, MetricType>> = buildList {
191153
for (aggregation in this@outputColumnNamesWithMetricTypes) {
@@ -227,3 +189,41 @@ internal fun List<AggregationSpec>.outputColumnNameToFeatureIdMap(): Map<String,
227189

228190
internal fun List<AggregationSpec>.outputColumnNames(): List<String> =
229191
outputColumnNamesWithMetricTypes().map { it.first }
192+
193+
internal fun AggregationSpec.toMetricDefinition():
194+
com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition {
195+
val metricType =
196+
when (this) {
197+
is Count -> MetricType.COUNT
198+
is PrivacyIdCount -> MetricType.PRIVACY_ID_COUNT
199+
else ->
200+
throw IllegalArgumentException("Unsupported AggregationSpec type for top-level metrics")
201+
}
202+
val budget =
203+
when (this) {
204+
is Count -> this.budget
205+
is PrivacyIdCount -> this.budget
206+
else ->
207+
throw IllegalArgumentException("Unsupported AggregationSpec type for top-level metrics")
208+
}
209+
return com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition(
210+
metricType,
211+
budget?.toInternalBudgetPerOpSpec(),
212+
)
213+
}
214+
215+
internal fun ValueAggregationSpec.toMetricDefinition():
216+
com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition {
217+
return com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition(
218+
this.metricType,
219+
this.budget?.toInternalBudgetPerOpSpec(),
220+
)
221+
}
222+
223+
internal fun VectorAggregationSpec.toMetricDefinition():
224+
com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition {
225+
return com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricDefinition(
226+
this.metricType,
227+
this.budget?.toInternalBudgetPerOpSpec(),
228+
)
229+
}

pipelinedp4j/main/com/google/privacy/differentialprivacy/pipelinedp4j/api/Query.kt

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ import com.google.privacy.differentialprivacy.pipelinedp4j.core.DpEngine
2323
import com.google.privacy.differentialprivacy.pipelinedp4j.core.DpEngineBudgetSpec
2424
import com.google.privacy.differentialprivacy.pipelinedp4j.core.Encoder
2525
import com.google.privacy.differentialprivacy.pipelinedp4j.core.EncoderFactory
26+
import com.google.privacy.differentialprivacy.pipelinedp4j.core.FeatureSpec
2627
import com.google.privacy.differentialprivacy.pipelinedp4j.core.FeatureValuesExtractor
2728
import com.google.privacy.differentialprivacy.pipelinedp4j.core.FrameworkCollection
2829
import com.google.privacy.differentialprivacy.pipelinedp4j.core.FrameworkTable
2930
import com.google.privacy.differentialprivacy.pipelinedp4j.core.MetricType
31+
import com.google.privacy.differentialprivacy.pipelinedp4j.core.ScalarFeatureSpec
3032
import com.google.privacy.differentialprivacy.pipelinedp4j.core.SelectPartitionsParams
3133
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.DpAggregates
32-
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.PerFeature
3334
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.copy
34-
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.dpAggregates
3535
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.perFeature
3636

3737
sealed interface Query<ReturnT> {
@@ -142,21 +142,6 @@ protected constructor(
142142
valueAndVectorAggs.map { it.getFeatureId() }
143143
}
144144
return aggResults
145-
.zip(featureIdPerRun)
146-
.map { (table, featureId) ->
147-
table.mapValues("TagWithFeatureId", encoderFactory.protos(DpAggregates::class)) { _, agg ->
148-
if (featureId == null) {
149-
agg
150-
} else {
151-
val perFeature = constructPerFeature(agg, featureId)
152-
dpAggregates {
153-
count = agg.count
154-
privacyIdCount = agg.privacyIdCount
155-
this.perFeature += perFeature
156-
}
157-
}
158-
}
159-
}
160145
.reduce {
161146
acc: FrameworkTable<GroupKeysT, DpAggregates>,
162147
table: FrameworkTable<GroupKeysT, DpAggregates> ->
@@ -496,41 +481,59 @@ protected constructor(
496481
): AggregationParams {
497482
val valueContributionBounds = valueAggregations?.contributionBounds
498483
val vectorContributionBounds = vectorAggregations?.vectorContributionBounds
484+
485+
val topLevelMetrics =
486+
aggregationSpecs
487+
.filter { it is Count || it is PrivacyIdCount }
488+
.map { it.toMetricDefinition() }
489+
val features =
490+
mutableListOf<com.google.privacy.differentialprivacy.pipelinedp4j.core.FeatureSpec>()
491+
if (valueAggregations != null) {
492+
features.add(
493+
com.google.privacy.differentialprivacy.pipelinedp4j.core.ScalarFeatureSpec(
494+
featureId = valueAggregations.getFeatureId(),
495+
metrics =
496+
ImmutableList.copyOf(
497+
valueAggregations.valueAggregationSpecs.map { it.toMetricDefinition() }
498+
),
499+
minValue = valueContributionBounds?.valueBounds?.minValue,
500+
maxValue = valueContributionBounds?.valueBounds?.maxValue,
501+
minTotalValue = valueContributionBounds?.totalValueBounds?.minValue,
502+
maxTotalValue = valueContributionBounds?.totalValueBounds?.maxValue,
503+
)
504+
)
505+
}
506+
if (vectorAggregations != null) {
507+
checkNotNull(vectorContributionBounds)
508+
features.add(
509+
com.google.privacy.differentialprivacy.pipelinedp4j.core.VectorFeatureSpec(
510+
featureId = vectorAggregations.getFeatureId(),
511+
metrics =
512+
ImmutableList.copyOf(
513+
vectorAggregations.vectorAggregationSpecs.map { it.toMetricDefinition() }
514+
),
515+
vectorSize =
516+
checkNotNull(vectorAggregations.vectorSize) {
517+
"vectorSize must be set for vector aggregations"
518+
},
519+
normKind = vectorContributionBounds.maxVectorTotalNorm.normKind.toInternalNormKind(),
520+
vectorMaxTotalNorm = vectorContributionBounds.maxVectorTotalNorm.value,
521+
)
522+
)
523+
}
524+
499525
return AggregationParams(
500-
metrics = ImmutableList.copyOf(aggregationSpecs.metrics()),
526+
metrics = ImmutableList.copyOf(topLevelMetrics),
527+
features = ImmutableList.copyOf(features),
501528
noiseKind =
502529
checkNotNull(noiseKind) { "noiseKind cannot be null if there are aggregations." }
503530
.toInternalNoiseKind(),
504531
maxPartitionsContributed = contributionBoundingLevel.getMaxPartitionsContributed(),
505532
maxContributionsPerPartition = contributionBoundingLevel.getMaxContributionsPerPartition(),
506-
minValue = valueContributionBounds?.valueBounds?.minValue,
507-
maxValue = valueContributionBounds?.valueBounds?.maxValue,
508-
minTotalValue = valueContributionBounds?.totalValueBounds?.minValue,
509-
maxTotalValue = valueContributionBounds?.totalValueBounds?.maxValue,
510-
vectorNormKind = vectorContributionBounds?.maxVectorTotalNorm?.normKind?.toInternalNormKind(),
511-
vectorMaxTotalNorm = vectorContributionBounds?.maxVectorTotalNorm?.value,
512-
vectorSize = vectorAggregations?.vectorSize,
513533
partitionSelectionBudget = groupsType.getBudget()?.toInternalBudgetPerOpSpec(),
514534
preThreshold = groupsType.getPreThreshold(),
515535
contributionBoundingLevel = contributionBoundingLevel.toInternalContributionBoundingLevel(),
516536
partitionsBalance = groupByAdditionalParameters.groupsBalance.toPartitionsBalance(),
517537
)
518538
}
519-
520-
companion object {
521-
private fun constructPerFeature(dpAggregates: DpAggregates, featureId: String): PerFeature {
522-
return perFeature {
523-
this.featureId = featureId
524-
sum = dpAggregates.sum
525-
mean = dpAggregates.mean
526-
variance = dpAggregates.variance
527-
if (dpAggregates.quantilesList.isNotEmpty()) {
528-
quantiles += dpAggregates.quantilesList
529-
}
530-
if (dpAggregates.vectorSumList.isNotEmpty()) {
531-
vectorSum += dpAggregates.vectorSumList
532-
}
533-
}
534-
}
535-
}
536539
}

pipelinedp4j/main/com/google/privacy/differentialprivacy/pipelinedp4j/api/QueryPerGroupResult.kt

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -115,40 +115,23 @@ internal constructor(
115115
MetricType.PRIVACY_ID_COUNT -> put(outputColumnName, dpAggregates.privacyIdCount)
116116
MetricType.COUNT -> put(outputColumnName, dpAggregates.count)
117117
MetricType.SUM -> {
118-
if (dpAggregates.perFeatureList.isNotEmpty()) {
119-
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
120-
put(outputColumnName, featuresMap[featureId]!!.sum)
121-
} else {
122-
put(outputColumnName, dpAggregates.sum)
123-
}
118+
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
119+
put(outputColumnName, featuresMap[featureId]!!.sum)
124120
}
125121
MetricType.VECTOR_SUM -> {} // not processed in this function.
126122
MetricType.MEAN -> {
127-
if (dpAggregates.perFeatureList.isNotEmpty()) {
128-
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
129-
put(outputColumnName, featuresMap[featureId]!!.mean)
130-
} else {
131-
put(outputColumnName, dpAggregates.mean)
132-
}
123+
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
124+
put(outputColumnName, featuresMap[featureId]!!.mean)
133125
}
134126
MetricType.VARIANCE -> {
135-
if (dpAggregates.perFeatureList.isNotEmpty()) {
136-
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
137-
put(outputColumnName, featuresMap[featureId]!!.variance)
138-
} else {
139-
put(outputColumnName, dpAggregates.variance)
140-
}
127+
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
128+
put(outputColumnName, featuresMap[featureId]!!.variance)
141129
}
142130
is MetricType.QUANTILES -> {
143131
// TODO: consider creating a data class or resuing copy of
144132
// DpAggregates proto and not allowing outputColumnName.
145-
val quantilesList =
146-
if (dpAggregates.perFeatureList.isNotEmpty()) {
147-
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
148-
featuresMap[featureId]!!.quantilesList
149-
} else {
150-
dpAggregates.quantilesList
151-
}
133+
val featureId = columnNameToFeatureIdMap[outputColumnName]!!
134+
val quantilesList = featuresMap[featureId]!!.quantilesList
152135
for ((rank, value) in metricType.sortedRanks.zip(quantilesList)) {
153136
put(outputColumnName.withRank(rank), value)
154137
}
@@ -171,12 +154,8 @@ internal constructor(
171154
MetricType.COUNT -> {} // not processed in this function.
172155
MetricType.SUM -> {} // not processed in this function.
173156
MetricType.VECTOR_SUM -> {
174-
if (dpAggregates.perFeatureList.isNotEmpty()) {
175-
val featureId = colNameToFeatureIdMap[outputColumnName]!!
176-
put(outputColumnName, featuresMap[featureId]!!.vectorSumList)
177-
} else {
178-
put(outputColumnName, dpAggregates.vectorSumList)
179-
}
157+
val featureId = colNameToFeatureIdMap[outputColumnName]!!
158+
put(outputColumnName, featuresMap[featureId]!!.vectorSumList)
180159
}
181160
MetricType.MEAN -> {} // not processed in this function.
182161
MetricType.VARIANCE -> {} // not processed in this function.

0 commit comments

Comments
 (0)