11use std:: collections:: HashMap ;
22
3- use crate :: budget:: pure_dp_filter:: PureDPBudget ;
4- use crate :: budget:: traits:: FilterStatus ;
5- use crate :: budget:: traits:: FilterStorage ;
6- use crate :: events:: traits:: RelevantEventSelector ;
7- use crate :: events:: traits:: { EpochEvents , EpochId , Event , EventStorage } ;
8- use crate :: mechanisms:: { NoiseScale , NormType } ;
9- use crate :: pds:: traits:: PrivateDataService ;
10- use crate :: queries:: traits:: {
11- EpochReportRequest , PassivePrivacyLossRequest , ReportRequest ,
3+ use crate :: {
4+ budget:: {
5+ pure_dp_filter:: PureDPBudget ,
6+ traits:: { FilterStatus , FilterStorage } ,
7+ } ,
8+ events:: traits:: {
9+ EpochEvents , EpochId , Event , EventStorage , RelevantEventSelector ,
10+ } ,
11+ mechanisms:: { NoiseScale , NormType } ,
12+ queries:: traits:: {
13+ EpochReportRequest , PassivePrivacyLossRequest , ReportRequest ,
14+ } ,
1215} ;
1316
14- /// Epoch-based private data service implementation, using generic filter
15- /// storage and event storage interfaces. We might want other implementations
16- /// eventually, but at first this implementation should cover most use cases,
17- /// as we can swap the types of events, filters and queries.
18- pub struct EpochPrivateDataServiceImpl <
17+ /// Epoch-based private data service, using generic filter
18+ /// storage and event storage interfaces.
19+ ///
20+ /// TODO(https://github.com/columbia/pdslib/issues/18): handle multiple queriers
21+ /// instead of assuming that there is a single querier and using filter_id =
22+ /// epoch_id
23+ pub struct EpochPrivateDataService <
1924 FS : FilterStorage ,
2025 ES : EventStorage ,
2126 Q : EpochReportRequest ,
@@ -31,20 +36,17 @@ pub struct EpochPrivateDataServiceImpl<
3136 pub epoch_capacity : FS :: Budget ,
3237
3338 /// Type of accepted queries.
34- pub _phantom : std:: marker:: PhantomData < Q > ,
39+ pub _phantom_request : std:: marker:: PhantomData < Q > ,
3540
3641 /// Type of errors.
3742 pub _phantom_error : std:: marker:: PhantomData < ERR > ,
3843}
3944
40- /// Implements the generic PDS interface for the epoch-based PDS.
45+ /// API for the epoch-based PDS.
4146///
4247/// TODO(https://github.com/columbia/pdslib/issues/21): support more than PureDP
43- /// TODO(https://github.com/columbia/pdslib/issues/18): handle multiple queriers
44- /// instead of assuming that there is a single querier and using filter_id =
45- /// epoch_id
46- impl < EI , E , EE , RES , FS , ES , Q , ERR > PrivateDataService
47- for EpochPrivateDataServiceImpl < FS , ES , Q , ERR >
48+ /// TODO(https://github.com/columbia/pdslib/issues/22): simplify trait bounds?
49+ impl < EI , E , EE , RES , FS , ES , Q , ERR > EpochPrivateDataService < FS , ES , Q , ERR >
4850where
4951 EI : EpochId ,
5052 E : Event < EpochId = EI > ,
@@ -59,25 +61,22 @@ where
5961 > ,
6062 ERR : From < FS :: Error > + From < ES :: Error > ,
6163{
62- type Event = E ;
63- type Request = Q ;
64- type PassivePrivacyLossRequest =
65- PassivePrivacyLossRequest < EI , PureDPBudget > ;
66- type Error = ERR ;
67-
68- fn register_event ( & mut self , event : E ) -> Result < ( ) , Self :: Error > {
64+ /// Registers a new event.
65+ pub fn register_event ( & mut self , event : E ) -> Result < ( ) , ERR > {
6966 println ! ( "Registering event {:?}" , event) ;
7067 self . event_storage . add_event ( event) ?;
7168 Ok ( ( ) )
7269 }
7370
71+ /// Computes a report for the given report request.
7472 /// This function follows `compute_attribution_report` from the Cookie
7573 /// Monster Algorithm (https://arxiv.org/pdf/2405.16719, Code Listing 1)
76- fn compute_report (
74+ pub fn compute_report (
7775 & mut self ,
7876 request : Q ,
79- ) -> Result < <Q as ReportRequest >:: Report , Self :: Error > {
77+ ) -> Result < <Q as ReportRequest >:: Report , ERR > {
8078 println ! ( "Computing report for request {:?}" , request) ;
79+
8180 // Collect events from event storage. If an epoch has no relevant
8281 // events, don't add it to the mapping.
8382 let mut relevant_events_per_epoch: HashMap < EI , EE > = HashMap :: new ( ) ;
@@ -118,7 +117,8 @@ where
118117 // TODO(https://github.com/columbia/pdslib/issues/18): handle multiple queriers.
119118 self . initialize_filter_if_necessary ( & epoch_id) ?;
120119
121- // Step 3. Try to consume budget from current epoch, drop events if OOB.
120+ // Step 3. Try to consume budget from current epoch, drop events if
121+ // OOB.
122122 match self
123123 . filter_storage
124124 . check_and_consume ( & epoch_id, & individual_privacy_loss)
@@ -143,10 +143,16 @@ where
143143 Ok ( filtered_report)
144144 }
145145
146- fn account_for_passive_privacy_loss (
146+ /// [Experimental] Accounts for passive privacy loss. Can fail if the
147+ /// implementation has an error, but failure must not leak the state of
148+ /// the filters.
149+ ///
150+ /// TODO(https://github.com/columbia/pdslib/issues/16): what are the semantics of passive loss queries that go over the filter
151+ /// capacity?
152+ pub fn account_for_passive_privacy_loss (
147153 & mut self ,
148- request : Self :: PassivePrivacyLossRequest ,
149- ) -> Result < FilterStatus , Self :: Error > {
154+ request : PassivePrivacyLossRequest < EI , PureDPBudget > ,
155+ ) -> Result < FilterStatus , ERR > {
150156 // For each epoch, try to consume the privacy budget.
151157 for epoch_id in request. epoch_ids {
152158 self . initialize_filter_if_necessary ( & epoch_id) ?;
@@ -165,19 +171,7 @@ where
165171 }
166172 Ok ( FilterStatus :: Continue )
167173 }
168- }
169174
170- /// Utility methods for the epoch-based PDS implementation.
171- impl < EI , E , EE , FS , ES , Q , ERR > EpochPrivateDataServiceImpl < FS , ES , Q , ERR >
172- where
173- EI : EpochId ,
174- E : Event < EpochId = EI > ,
175- EE : EpochEvents ,
176- FS : FilterStorage < FilterId = EI > ,
177- ES : EventStorage < Event = E , EpochEvents = EE > ,
178- Q : EpochReportRequest < EpochId = EI , EpochEvents = EE > ,
179- ERR : From < FS :: Error > + From < ES :: Error > ,
180- {
181175 fn initialize_filter_if_necessary (
182176 & mut self ,
183177 epoch_id : & EI ,
@@ -244,8 +238,8 @@ where
244238 return PureDPBudget :: Infinite ;
245239 }
246240
247- // In Cookie Monster, we have `query_global_sensitivity` / `requested_epsilon` instead
248- // of just `noise_scale`.
241+ // In Cookie Monster, we have `query_global_sensitivity` /
242+ // `requested_epsilon` instead of just `noise_scale`.
249243 // TODO(https://github.com/columbia/pdslib/issues/23): potentially use two parameters
250244 // instead of a single `noise_scale`.
251245 PureDPBudget :: Epsilon ( individual_sensitivity / noise_scale)
@@ -276,11 +270,13 @@ mod tests {
276270 > = HashMapFilterStorage :: new ( ) ;
277271 let events = HashMapEventStorage :: new ( ) ;
278272
279- let mut pds = EpochPrivateDataServiceImpl {
273+ let mut pds = EpochPrivateDataService {
280274 filter_storage : filters,
281275 event_storage : events,
282276 epoch_capacity : PureDPBudget :: Epsilon ( 3.0 ) ,
283- _phantom : std:: marker:: PhantomData :: < SimpleLastTouchHistogramRequest > ,
277+ _phantom_request : std:: marker:: PhantomData :: <
278+ SimpleLastTouchHistogramRequest ,
279+ > ,
284280 _phantom_error : std:: marker:: PhantomData :: < anyhow:: Error > ,
285281 } ;
286282
0 commit comments