use crate::errors::*;
use crate::{proto, base};
use crate::components::{Expandable, Report};
use crate::base::{IndexKey, NodeProperties, Value};
use crate::utilities::json::{JSONRelease, value_to_json, AlgorithmInfo, privacy_usage_to_json};
use std::convert::TryFrom;
use crate::utilities::prepend;
use indexmap::map::IndexMap;
impl Expandable for proto::DpCovariance {
fn expand_component(
&self,
_privacy_definition: &Option<proto::PrivacyDefinition>,
component: &proto::Component,
_public_arguments: &IndexMap<IndexKey, &Value>,
properties: &base::NodeProperties,
component_id: u32,
mut maximum_id: u32,
) -> Result<base::ComponentExpansion> {
let mut expansion = base::ComponentExpansion::default();
let arguments;
let shape;
let symmetric;
match properties.get(&IndexKey::from("data")) {
Some(data_property) => {
let data_property = data_property.array()
.map_err(prepend("data:"))?.clone();
let num_columns = data_property.num_columns()?;
shape = vec![u32::try_from(num_columns)?, u32::try_from(num_columns)?];
arguments = indexmap![
"data".into() => *component.arguments().get::<IndexKey>(&"data".into())
.ok_or_else(|| Error::from("data must be provided as an argument"))?
];
symmetric = true;
},
None => {
let left_property = properties.get::<IndexKey>(&"left".into())
.ok_or("data: missing")?.array()
.map_err(prepend("data:"))?.clone();
let right_property = properties.get::<IndexKey>(&"right".into())
.ok_or("data: missing")?.array()
.map_err(prepend("data:"))?.clone();
shape = vec![u32::try_from(left_property.num_columns()?)?, u32::try_from(right_property.num_columns()?)?];
arguments = indexmap![
"left".into() => *component.arguments().get::<IndexKey>(&"left".into())
.ok_or_else(|| Error::from("left must be provided as an argument"))?,
"right".into() => *component.arguments().get::<IndexKey>(&"right".into())
.ok_or_else(|| Error::from("right must be provided as an argument"))?
];
symmetric = false;
}
};
maximum_id += 1;
let id_covariance = maximum_id;
expansion.computation_graph.insert(id_covariance, proto::Component {
arguments: Some(proto::ArgumentNodeIds::new(arguments)),
variant: Some(proto::component::Variant::Covariance(proto::Covariance {
finite_sample_correction: self.finite_sample_correction
})),
omit: true,
submission: component.submission,
});
expansion.traversal.push(id_covariance);
maximum_id += 1;
let id_noise = maximum_id;
expansion.computation_graph.insert(id_noise, proto::Component {
arguments: Some(proto::ArgumentNodeIds::new(indexmap!["data".into() => id_covariance])),
variant: Some(match self.mechanism.to_lowercase().as_str() {
"laplace" => proto::component::Variant::LaplaceMechanism(proto::LaplaceMechanism {
privacy_usage: self.privacy_usage.clone()
}),
"gaussian" => proto::component::Variant::GaussianMechanism(proto::GaussianMechanism {
privacy_usage: self.privacy_usage.clone()
}),
_x => panic!("Unexpected invalid token {:?}", self.mechanism.as_str()),
}),
omit: true,
submission: component.submission,
});
expansion.traversal.push(id_noise);
expansion.computation_graph.insert(component_id, proto::Component {
arguments: Some(proto::ArgumentNodeIds::new(indexmap!["data".into() => id_noise])),
variant: Some(proto::component::Variant::Reshape(proto::Reshape {
symmetric,
layout: "row".to_string(),
shape
})),
omit: component.omit,
submission: component.submission
});
Ok(expansion)
}
}
impl Report for proto::DpCovariance {
fn summarize(
&self,
node_id: u32,
component: &proto::Component,
_public_arguments: IndexMap<base::IndexKey, &Value>,
properties: NodeProperties,
release: &Value,
variable_names: Option<&Vec<base::IndexKey>>,
) -> Result<Option<Vec<JSONRelease>>> {
let argument;
let statistic;
if properties.contains_key(&IndexKey::from("data")) {
let data_property = properties.get::<IndexKey>(&"data".into())
.ok_or("data: missing")?.array()
.map_err(prepend("data:"))?.clone();
statistic = "DPCovariance".to_string();
argument = serde_json::json!({
"n": data_property.num_records()?,
"constraint": {
"lowerbound": data_property.lower_float()?,
"upperbound": data_property.upper_float()?
}
});
}
else {
let left_property = properties.get::<IndexKey>(&"left".into())
.ok_or("data: missing")?.array()
.map_err(prepend("data:"))?.clone();
let right_property = properties.get::<IndexKey>(&"right".into())
.ok_or("data: missing")?.array()
.map_err(prepend("data:"))?.clone();
statistic = "DPCrossCovariance".to_string();
argument = serde_json::json!({
"n": left_property.num_records()?,
"constraint": {
"lowerbound_left": left_property.lower_float()?,
"upperbound_left": left_property.upper_float()?,
"lowerbound_right": right_property.lower_float()?,
"upperbound_right": right_property.upper_float()?
}
});
}
let privacy_usage: Vec<serde_json::Value> = self.privacy_usage.iter()
.map(privacy_usage_to_json).clone().collect();
Ok(Some(vec![JSONRelease {
description: "DP release information".to_string(),
statistic,
variables: serde_json::json!(variable_names.cloned()
.unwrap_or_else(Vec::new).iter()
.map(|v| v.to_string()).collect::<Vec<String>>()),
release_info: value_to_json(&release)?,
privacy_loss: serde_json::json![privacy_usage],
accuracy: None,
submission: component.submission,
node_id,
postprocess: false,
algorithm_info: AlgorithmInfo {
name: "".to_string(),
cite: "".to_string(),
mechanism: self.mechanism.clone(),
argument
}
}]))
}
}