use crate::{
core::{Domain, Function, Measurement, Metric, MetricSpace},
domains::{AtomDomain, VectorDomain},
error::Fallible,
measures::MaxDivergence,
metrics::{AbsoluteDistance, L1Distance},
traits::{samplers::SampleDiscreteLaplaceLinear, CheckAtom},
traits::{Float, InfCast, Integer},
};
#[cfg(feature = "use-mpfr")]
use opendp_derive::bootstrap;
#[cfg(feature = "use-mpfr")]
use az::SaturatingCast;
#[cfg(feature = "ffi")]
mod ffi;
#[cfg(feature = "use-mpfr")]
mod cks20;
#[cfg(feature = "use-mpfr")]
pub use cks20::*;
mod linear;
pub use linear::*;
#[doc(hidden)]
pub trait MappableDomain: Domain {
type Atom: Clone;
fn map_over(
arg: &Self::Carrier,
func: &impl Fn(&Self::Atom) -> Fallible<Self::Atom>,
) -> Fallible<Self::Carrier>;
fn new_map_function(
func: impl Fn(&Self::Atom) -> Fallible<Self::Atom> + 'static,
) -> Function<Self::Carrier, Self::Carrier> {
Function::new_fallible(move |arg: &Self::Carrier| Self::map_over(arg, &func))
}
}
impl<T: Clone + CheckAtom> MappableDomain for AtomDomain<T> {
type Atom = T;
fn map_over(
arg: &Self::Carrier,
func: &impl Fn(&Self::Atom) -> Fallible<Self::Atom>,
) -> Fallible<Self::Carrier> {
(func)(arg)
}
}
impl<D: MappableDomain> MappableDomain for VectorDomain<D> {
type Atom = D::Atom;
fn map_over(
arg: &Vec<D::Carrier>,
func: &impl Fn(&Self::Atom) -> Fallible<Self::Atom>,
) -> Fallible<Self::Carrier> {
arg.iter().map(|v| D::map_over(v, func)).collect()
}
}
#[doc(hidden)]
pub trait DiscreteLaplaceDomain: MappableDomain + Default {
type InputMetric: Metric<Distance = Self::Atom> + Default;
}
impl<T: Clone + CheckAtom> DiscreteLaplaceDomain for AtomDomain<T> {
type InputMetric = AbsoluteDistance<T>;
}
impl<T: Clone + CheckAtom> DiscreteLaplaceDomain for VectorDomain<AtomDomain<T>> {
type InputMetric = L1Distance<T>;
}
#[bootstrap(
features("contrib"),
arguments(scale(c_type = "void *")),
generics(D(default = "AtomDomain<int>"))
)]
#[cfg(feature = "use-mpfr")]
pub fn make_base_discrete_laplace<D, QO>(
scale: QO,
) -> Fallible<Measurement<D, D::Carrier, D::InputMetric, MaxDivergence<QO>>>
where
D: DiscreteLaplaceDomain,
D::Atom: Integer + SampleDiscreteLaplaceLinear<QO>,
(D, D::InputMetric): MetricSpace,
QO: Float + InfCast<D::Atom> + InfCast<D::Atom>,
rug::Rational: std::convert::TryFrom<QO>,
rug::Integer: From<D::Atom> + SaturatingCast<D::Atom>,
{
if scale > QO::exact_int_cast(10)? {
make_base_discrete_laplace_cks20(scale)
} else {
make_base_discrete_laplace_linear(scale, None)
}
}
#[cfg(not(feature = "use-mpfr"))]
pub fn make_base_discrete_laplace<D, QO>(
scale: QO,
) -> Fallible<Measurement<D, D::Carrier, D::InputMetric, MaxDivergence<QO>>>
where
D: DiscreteLaplaceDomain,
(D, D::InputMetric): MetricSpace,
D::Atom: Integer + SampleDiscreteLaplaceLinear<QO>,
QO: Float + InfCast<D::Atom>,
{
make_base_discrete_laplace_linear(scale, None)
}
#[cfg(test)]
mod test {
use super::*;
use crate::domains::AtomDomain;
#[test]
fn test_make_base_discrete_laplace() -> Fallible<()> {
let meas = make_base_discrete_laplace::<AtomDomain<_>, _>(1f64)?;
println!("{:?}", meas.invoke(&0)?);
assert!(meas.check(&1, &1.)?);
Ok(())
}
}