#[cfg(feature = "ffi")]
mod ffi;
use opendp_derive::bootstrap;
use crate::core::{Domain, Function, Metric, MetricSpace, StabilityMap, Transformation};
use crate::domains::{AtomDomain, VectorDomain};
use crate::error::*;
use crate::metrics::EventLevelMetric;
use crate::traits::{CheckAtom, CheckNull};
pub trait DatasetDomain: Domain {
type ElementDomain: Domain;
}
impl<D: Domain> DatasetDomain for VectorDomain<D> {
type ElementDomain = D;
}
pub trait RowByRowDomain<DO: DatasetDomain>: DatasetDomain {
fn translate(&self, output_row_domain: DO::ElementDomain) -> DO;
fn apply_rows(
value: &Self::Carrier,
row_function: &impl Fn(
&<Self::ElementDomain as Domain>::Carrier,
) -> Fallible<<DO::ElementDomain as Domain>::Carrier>,
) -> Fallible<DO::Carrier>;
}
impl<DIA: Domain, DOA: Domain> RowByRowDomain<VectorDomain<DOA>> for VectorDomain<DIA> {
fn translate(
&self,
output_row_domain: <VectorDomain<DOA> as DatasetDomain>::ElementDomain,
) -> VectorDomain<DOA> {
VectorDomain {
element_domain: output_row_domain,
size: self.size,
}
}
fn apply_rows(
value: &Self::Carrier,
row_function: &impl Fn(&DIA::Carrier) -> Fallible<DOA::Carrier>,
) -> Fallible<Vec<DOA::Carrier>> {
value.iter().map(row_function).collect()
}
}
pub(crate) fn make_row_by_row<DI, DO, M>(
input_domain: DI,
input_metric: M,
output_row_domain: DO::ElementDomain,
row_function: impl 'static
+ Fn(
&<DI::ElementDomain as Domain>::Carrier,
) -> <DO::ElementDomain as Domain>::Carrier
+ Send
+ Sync,
) -> Fallible<Transformation<DI, M, DO, M>>
where
DI: RowByRowDomain<DO>,
DO: DatasetDomain,
M: EventLevelMetric,
(DI, M): MetricSpace,
(DO, M): MetricSpace,
{
let row_function = move |arg: &<DI::ElementDomain as Domain>::Carrier| Ok(row_function(arg));
make_row_by_row_fallible(input_domain, input_metric, output_row_domain, row_function)
}
pub(crate) fn make_row_by_row_fallible<DI, DO, M>(
input_domain: DI,
input_metric: M,
output_row_domain: DO::ElementDomain,
row_function: impl 'static
+ Fn(
&<DI::ElementDomain as Domain>::Carrier,
) -> Fallible<<DO::ElementDomain as Domain>::Carrier>
+ Send
+ Sync,
) -> Fallible<Transformation<DI, M, DO, M>>
where
DI: RowByRowDomain<DO>,
DO: DatasetDomain,
M: EventLevelMetric,
(DI, M): MetricSpace,
(DO, M): MetricSpace,
{
let output_domain = input_domain.translate(output_row_domain);
Transformation::new(
input_domain,
input_metric.clone(),
output_domain,
input_metric,
Function::new_fallible(move |arg: &DI::Carrier| DI::apply_rows(arg, &row_function)),
StabilityMap::new_from_constant(1),
)
}
#[bootstrap(
features("contrib", "honest-but-curious"),
generics(D(suppress), M(suppress))
)]
pub fn make_identity<D, M>(domain: D, metric: M) -> Fallible<Transformation<D, M, D, M>>
where
D: Domain,
D::Carrier: Clone,
M: Metric,
M::Distance: Clone,
(D, M): MetricSpace,
{
Transformation::new(
domain.clone(),
metric.clone(),
domain,
metric,
Function::new(|arg: &D::Carrier| arg.clone()),
StabilityMap::new(|d_in: &M::Distance| d_in.clone()),
)
}
#[bootstrap(
features("contrib"),
arguments(
input_domain(c_type = "AnyDomain *"),
input_metric(c_type = "AnyMetric *")
),
generics(TIA(suppress), M(suppress)),
derived_types(
TIA = "$get_atom(get_type(input_domain))",
M = "$get_type(input_metric)"
)
)]
pub fn make_is_equal<TIA, M>(
input_domain: VectorDomain<AtomDomain<TIA>>,
input_metric: M,
value: TIA,
) -> Fallible<Transformation<VectorDomain<AtomDomain<TIA>>, M, VectorDomain<AtomDomain<bool>>, M>>
where
TIA: 'static + PartialEq + CheckAtom,
M: EventLevelMetric,
(VectorDomain<AtomDomain<TIA>>, M): MetricSpace,
(VectorDomain<AtomDomain<bool>>, M): MetricSpace,
{
make_row_by_row(
input_domain,
input_metric,
AtomDomain::default(),
move |v| v == &value,
)
}
#[bootstrap(features("contrib"), generics(M(suppress), DIA(suppress)))]
pub fn make_is_null<M, DIA>(
input_domain: VectorDomain<DIA>,
input_metric: M,
) -> Fallible<Transformation<VectorDomain<DIA>, M, VectorDomain<AtomDomain<bool>>, M>>
where
DIA: Domain,
DIA::Carrier: 'static + CheckNull,
M: EventLevelMetric,
(VectorDomain<DIA>, M): MetricSpace,
(VectorDomain<AtomDomain<bool>>, M): MetricSpace,
{
make_row_by_row(input_domain, input_metric, AtomDomain::default(), |v| {
v.is_null()
})
}
#[cfg(test)]
mod test;