Skip to content

Commit 903fffc

Browse files
alexojicacopybara-github
authored andcommitted
Refactor contribution sampling and combiners to work with multiple features.
PiperOrigin-RevId: 811408440
1 parent 082fbaf commit 903fffc

33 files changed

Lines changed: 2637 additions & 1125 deletions

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

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -148,58 +148,20 @@ 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) {
192154
when (aggregation) {
193-
is PrivacyIdCount -> add(aggregation.outputColumnName to MetricType.PRIVACY_ID_COUNT)
194-
is Count -> add(aggregation.outputColumnName to MetricType.COUNT)
155+
is PrivacyIdCount -> add(Pair(aggregation.outputColumnName, MetricType.PRIVACY_ID_COUNT))
156+
is Count -> add(Pair(aggregation.outputColumnName, MetricType.COUNT))
195157
is ValueAggregations<*> -> {
196158
for (valueAggregationSpec in aggregation.valueAggregationSpecs) {
197-
add(valueAggregationSpec.outputColumnName to valueAggregationSpec.metricType)
159+
add(Pair(valueAggregationSpec.outputColumnName, valueAggregationSpec.metricType))
198160
}
199161
}
200162
is VectorAggregations<*> -> {
201163
for (vectorAggregationSpec in aggregation.vectorAggregationSpecs) {
202-
add(vectorAggregationSpec.outputColumnName to vectorAggregationSpec.metricType)
164+
add(Pair(vectorAggregationSpec.outputColumnName, vectorAggregationSpec.metricType))
203165
}
204166
}
205167
}
@@ -227,3 +189,22 @@ 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.toNonFeatureMetricDefinition(): MetricDefinition {
194+
val (metricType, budget) =
195+
when (this) {
196+
is Count -> Pair(MetricType.COUNT, this.budget)
197+
is PrivacyIdCount -> Pair(MetricType.PRIVACY_ID_COUNT, this.budget)
198+
else ->
199+
throw IllegalArgumentException("Unsupported AggregationSpec type for non feature metrics")
200+
}
201+
return MetricDefinition(metricType, budget?.toInternalBudgetPerOpSpec())
202+
}
203+
204+
internal fun ValueAggregationSpec.toMetricDefinition(): MetricDefinition {
205+
return MetricDefinition(this.metricType, this.budget?.toInternalBudgetPerOpSpec())
206+
}
207+
208+
internal fun VectorAggregationSpec.toMetricDefinition(): MetricDefinition {
209+
return MetricDefinition(this.metricType, this.budget?.toInternalBudgetPerOpSpec())
210+
}

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

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ 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
33+
import com.google.privacy.differentialprivacy.pipelinedp4j.core.VectorFeatureSpec
3134
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.DpAggregates
32-
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.PerFeature
3335
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.copy
34-
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.dpAggregates
35-
import com.google.privacy.differentialprivacy.pipelinedp4j.proto.perFeature
3636

3737
sealed interface Query<ReturnT> {
3838
/** Executes the query (in production mode). */
@@ -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> ->
@@ -494,43 +479,59 @@ protected constructor(
494479
valueAggregations: ValueAggregations<*>?,
495480
vectorAggregations: VectorAggregations<*>?,
496481
): AggregationParams {
497-
val valueContributionBounds = valueAggregations?.contributionBounds
498-
val vectorContributionBounds = vectorAggregations?.vectorContributionBounds
482+
val nonFeatureMetrics =
483+
aggregationSpecs
484+
.filter { it is Count || it is PrivacyIdCount }
485+
.map { it.toNonFeatureMetricDefinition() }
486+
val features =
487+
buildList<FeatureSpec> {
488+
if (valueAggregations != null) {
489+
val valueContributionBounds = valueAggregations.contributionBounds
490+
add(
491+
ScalarFeatureSpec(
492+
featureId = valueAggregations.getFeatureId(),
493+
metrics =
494+
valueAggregations.valueAggregationSpecs
495+
.map { it.toMetricDefinition() }
496+
.toImmutableList(),
497+
minValue = valueContributionBounds.valueBounds?.minValue,
498+
maxValue = valueContributionBounds.valueBounds?.maxValue,
499+
minTotalValue = valueContributionBounds.totalValueBounds?.minValue,
500+
maxTotalValue = valueContributionBounds.totalValueBounds?.maxValue,
501+
)
502+
)
503+
}
504+
if (vectorAggregations != null) {
505+
val vectorContributionBounds = vectorAggregations.vectorContributionBounds
506+
add(
507+
VectorFeatureSpec(
508+
featureId = vectorAggregations.getFeatureId(),
509+
metrics =
510+
vectorAggregations.vectorAggregationSpecs
511+
.map { it.toMetricDefinition() }
512+
.toImmutableList(),
513+
vectorSize = vectorAggregations.vectorSize,
514+
normKind = vectorContributionBounds.maxVectorTotalNorm.normKind.toInternalNormKind(),
515+
vectorMaxTotalNorm = vectorContributionBounds.maxVectorTotalNorm.value,
516+
)
517+
)
518+
}
519+
}
520+
499521
return AggregationParams(
500-
metrics = ImmutableList.copyOf(aggregationSpecs.metrics()),
522+
nonFeatureMetrics = nonFeatureMetrics.toImmutableList(),
523+
features = features.toImmutableList(),
501524
noiseKind =
502525
checkNotNull(noiseKind) { "noiseKind cannot be null if there are aggregations." }
503526
.toInternalNoiseKind(),
504527
maxPartitionsContributed = contributionBoundingLevel.getMaxPartitionsContributed(),
505528
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,
513529
partitionSelectionBudget = groupsType.getBudget()?.toInternalBudgetPerOpSpec(),
514530
preThreshold = groupsType.getPreThreshold(),
515531
contributionBoundingLevel = contributionBoundingLevel.toInternalContributionBoundingLevel(),
516532
partitionsBalance = groupByAdditionalParameters.groupsBalance.toPartitionsBalance(),
517533
)
518534
}
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-
}
536535
}
536+
537+
private fun <T : Any> Iterable<T>.toImmutableList(): ImmutableList<T> = ImmutableList.copyOf(this)

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)