In [1]:
import torch as th
In [2]:
class PrivateTensor():
def __init__(self, values, max_vals, min_vals):
self.values = values
self.max_vals = max_vals
self.min_vals = min_vals
def __add__(self, other):
# add to a private number
if(isinstance(other, PrivateTensor)):
new_vals = self.values + other.values
new_max_vals = self.max_vals + other.max_vals
new_min_vals = self.min_vals + other.min_vals
else:
# add to a public number
new_vals = self.values + other
new_max_vals = self.max_vals + other
new_min_vals = self.min_vals + other
return PrivateTensor(new_vals,
new_max_vals,
new_min_vals)
def __sub__(self, other):
# add to a private number
if(isinstance(other, PrivateTensor)):
new_vals = self.values - other.values
# note that other.max/min values are reversed on purpose
# because this functionality is equivalent to
# output = self + (other * -1) and multiplication by
# a negative number swaps the max/min values with each
# other and flips their sign
new_max_vals = self.max_vals - other.min_vals
new_min_vals = self.min_vals - other.max_vals
else:
# add to a public number
new_vals = self.values - other
new_max_vals = self.max_vals - other
new_min_vals = self.min_vals - other
return PrivateTensor(new_vals,
new_max_vals,
new_min_vals)
def __mul__(self, other):
if(isinstance(other, PrivateTensor)):
""
new_vals = self.values * other.values
new_self_max_vals = th.max(self.min_vals * other.expanded_minminvals,
self.max_vals * other.expanded_maxmaxvals)
new_self_min_vals = th.min(self.min_vals * other.expanded_maxmaxvals,
self.max_vals * other.expanded_minminvals)
new_other_max_vals = th.max(other.min_vals * self.expanded_minminvals,
other.max_vals * self.expanded_maxmaxvals)
new_other_min_vals = th.max(other.min_vals * self.expanded_maxmaxvals,
other.max_vals * self.expanded_minminvals)
new_max_vals = th.max(new_self_max_vals, new_other_max_vals)
new_min_vals = th.max(new_self_min_vals, new_other_min_vals)
else:
# add to a public number
new_vals = self.values * other
if(other > 0):
new_max_vals = self.max_vals * other
new_min_vals = self.min_vals * other
else:
new_min_vals = self.max_vals * other
new_max_vals = self.min_vals * other
return PrivateTensor(new_vals,
new_max_vals,
new_min_vals)
def __neg__(self):
# note that new_min_vals and new_max_vals are reversed intentionally
return PrivateTensor(-self.values,
-self.min_vals,
-self.max_vals)
def __truediv__(self, other):
if(isinstance(other, PrivateTensor)):
raise Exception("probably best not to do this - it's gonna be inf a lot")
new_vals = self.values / other
new_max_vals = self.max_vals / other
new_min_vals = self.min_vals / other
return PrivateTensor(new_vals,
new_max_vals,
new_min_vals)
def __gt__(self, other):
"""BUG: the zero values mess this up"""
if(isinstance(other, PrivateTensor)):
new_vals = self.values > other.values
# if self is bigger than the biggest possible other
if_left = self.min_vals > other.expanded_maxmaxvals
# if self is smaller than the smallest possible other
if_right = self.max_vals < other.expanded_minminvals
# if self doesn't overlap with other at all
if_left_or_right = if_left + if_right # shouldn't have to check if this > 2 assuming
# other's max is > other's min
# if self does overlap with other
new_self_max_val = 1 - if_left_or_right
# can't have a threshold output less than 0
new_self_min_val = if_left_or_right * 0
# if other is bigger than the smallest possible self
if_left = other.min_vals > self.expanded_maxmaxvals
# if other is smaller than the smallest possible self
if_right = other.max_vals < self.expanded_minminvals
# if other and self don't overlap
if_left_or_right = if_left + if_right # shouldn't have to check if this > 2 assuming
# other's max is > other's min
# if other and self do overlap
new_other_max_val = 1 - if_left_or_right
# the smallest possible result is 0
new_other_min_val = new_self_min_val + 0
new_max_val = th.max(new_self_max_val, new_other_max_val)
new_min_val = th.max(new_self_min_val, new_other_min_val)
else:
new_vals = self.values > other
if_left = other <= self.max_vals
if_right = other >= self.min_vals
if_and = if_left * if_right
new_max_val = if_and
new_min_val = new_max_val * 0
return PrivateTensor(new_vals,
new_max_val,
new_min_val)
@property
def maxmaxvals(self):
"""This returns the maximum possible value over all entities"""
"""BUG: the zero values mess this up, enforcing a min max of 0"""
return self.max_vals.max(1)[0]
@property
def expanded_maxmaxvals(self):
return self.maxmaxvals.unsqueeze(1).expand(self.max_vals.shape)
@property
def minminvals(self):
"""This returns the minimum possible values over all entities"""
"""BUG: the zero values mess this up, enforcing a max min of 0"""
return self.min_vals.min(1)[0]
@property
def expanded_minminvals(self):
return self.minminvals.unsqueeze(1).expand(self.min_vals.shape)
@property
def sensitivity(self):
return (self.max_vals - self.min_vals).max(1)[0]
def hard_sigmoid(self):
return self.clamp_max(1).clamp_min(0)
def hard_sigmoid_deriv(self, leak=0.01):
return ((self < 1) * (self > 0)) + (self < 0) * leak - (self > 1) * leak
In [ ]:
In [123]:
data = th.tensor([2.,0,1,0,0,0])
max_vals = th.tensor([[3.,0,0,0,0], [0,1,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,0,0,0,1], [0,0,0,0,1]])
min_vals = th.tensor([[2.,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0]])
x = PrivateTensor(data, max_vals, min_vals)
In [124]:
data = th.tensor([0.,1,1,1,1, 0])
max_vals = th.tensor([[1.,0,0,0,0], [0,1,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,0,0,0,1], [0,0,0,0,1]])
min_vals = th.tensor([[0.,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,1]])
y = PrivateTensor(data, max_vals, min_vals)
In [125]:
z = y > x
In [126]:
x.max_vals
Out[126]:
In [127]:
x.min_vals
Out[127]:
In [113]:
z.values
Out[113]:
In [114]:
z.max_vals
Out[114]:
In [116]:
z.min_vals
Out[116]:
In [108]:
z.minminvals
Out[108]:
In [105]:
z.sensitivity
Out[105]:
In [ ]:
In [1]:
from collections import Counter
import numpy as np
class PrivateNumber():
def __init__(self, value, max_val, min_val):
self.value = value
self.max_val = max_val
self.min_val = min_val
def __add__(self, other):
# add to a private number
if(isinstance(other, PrivateNumber)):
entities = self.entities.union(other.entities)
new_val = self.value + other.value
entities = set(self.max_val.keys()).union(set(other.max_val.keys()))
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_max_val[entity] = self.max_val[entity] + other.max_val[entity]
new_min_val[entity] = self.min_val[entity] + other.min_val[entity]
return PrivateNumber(self.value + other.value,
new_max_val,
new_min_val)
entities = self.entities
# add to a public number
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_max_val[entity] = self.max_val[entity] + other
new_min_val[entity] = self.min_val[entity] + other
return PrivateNumber(self.value + other,
new_max_val,
new_min_val)
def __sub__(self, other):
return self + (-other)
def __mul__(self, other):
if(isinstance(other, PrivateNumber)):
entities = self.entities.union(other.entities)
new_self_max_val = Counter()
new_self_min_val = Counter()
for entity in entities:
# the biggest positive number this entity could contribute is when
# it is multiplied by the largest value of the same sign from other
new_self_max_val[entity] = max(self.min_val[entity] * other.xmin,
self.max_val[entity] * other.xmax)
# the smallest negative number this entity could contribute is when
# it is multiplied by the largest value of the opposite sign from other
new_self_min_val[entity] = min(self.min_val[entity] * other.xmax,
self.max_val[entity] * other.xmin)
new_other_max_val = Counter()
new_other_min_val = Counter()
for entity in entities:
# the biggest positive number this entity could contribute is when
# it is multiplied by the largest value of the same sign from other
new_other_max_val[entity] = max(other.min_val[entity] * self.xmin,
other.max_val[entity] * self.xmax)
# the smallest negative number this entity could contribute is when
# it is multiplied by the largest value of the opposite sign from other
new_other_min_val[entity] = min(other.min_val[entity] * self.xmax,
other.max_val[entity] * self.xmin)
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_max_val[entity] = max(new_self_max_val[entity], new_other_max_val[entity])
new_min_val[entity] = min(new_self_min_val[entity], new_other_min_val[entity])
return PrivateNumber(self.value * other.value,
new_max_val,
new_min_val)
entities = self.entities
new_max_val = Counter()
for entity in entities:
new_max_val[entity] = self.max_val[entity] * other
new_min_val = Counter()
for entity in entities:
new_min_val[entity] = self.min_val[entity] * other
if(other > 0):
return PrivateNumber(self.value * other,
new_max_val,
new_min_val)
else:
return PrivateNumber(self.value * other,
new_min_val,
new_max_val)
def __truediv__(self, other):
if(isinstance(other, PrivateNumber)):
raise Exception("probably best not to do this - it's gonna be inf a lot")
entities = self.entities
new_max_val = Counter()
for entity in entities:
new_max_val[entity] = self.max_val[entity] / other
new_min_val = Counter()
for entity in entities:
new_min_val[entity] = self.min_val[entity] / other
return PrivateNumber(self.value / other,
new_max_val,
new_min_val)
def __gt__(self, other):
if(isinstance(other, PrivateNumber)):
entities = self.entities.union(other.entities)
new_self_max_val = Counter()
new_self_min_val = Counter()
for entity in entities:
if not (self.min_val[entity] > other.xmax or self.max_val[entity] < other.xmin):
new_self_max_val[entity] = 1
else:
new_self_max_val[entity] = 0
new_self_min_val[entity] = 0
new_other_max_val = Counter()
new_other_min_val = Counter()
for entity in entities:
if not (other.min_val[entity] > self.xmax or other.max_val[entity] < self.xmin):
new_other_max_val[entity] = 1
else:
new_other_max_val[entity] = 0
new_other_min_val[entity] = 0
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_max_val[entity] = max(new_self_max_val[entity], new_other_max_val[entity])
new_min_val[entity] = min(new_self_min_val[entity], new_other_min_val[entity])
return PrivateNumber(int(self.value > other.value),
new_max_val,
new_min_val)
entities = self.entities
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_min_val[entity] = 0
if(other <= self.max_val[entity] and other >= self.min_val[entity]):
new_max_val[entity] = 1
else:
new_max_val[entity] = 0
return PrivateNumber(int(self.value > other),
new_max_val,
new_min_val)
def __lt__(self, other):
if(isinstance(other, PrivateNumber)):
entities = self.entities.union(other.entities)
new_self_max_val = Counter()
new_self_min_val = Counter()
for entity in entities:
if not (self.min_val[entity] > other.xmax or self.max_val[entity] < other.xmin):
new_self_max_val[entity] = 1
else:
new_self_max_val[entity] = 0
new_self_min_val[entity] = 0
new_other_max_val = Counter()
new_other_min_val = Counter()
for entity in entities:
if not (other.min_val[entity] > self.xmax or other.max_val[entity] < self.xmin):
new_other_max_val[entity] = 1
else:
new_other_max_val[entity] = 0
new_other_min_val[entity] = 0
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_max_val[entity] = max(new_self_max_val[entity], new_other_max_val[entity])
new_min_val[entity] = min(new_self_min_val[entity], new_other_min_val[entity])
return PrivateNumber(int(self.value < other.value),
new_max_val,
new_min_val)
entities = self.entities
new_max_val = Counter()
new_min_val = Counter()
for entity in entities:
new_min_val[entity] = 0
if(other <= self.max_val[entity] and other >= self.min_val[entity]):
new_max_val[entity] = 1
else:
new_max_val[entity] = 0
return PrivateNumber(int(self.value < other),
new_max_val,
new_min_val)
def __neg__(self):
return self * -1
def max(self, other):
if(isinstance(other, PrivateNumber)):
raise Exception("Not implemented yet")
entities = self.entities
new_min_val = Counter()
for entity in entities:
new_min_val[entity] = max(self.min_val[entity], other)
return PrivateNumber(max(self.value, other),
self.max_val,
new_min_val)
def min(self, other):
if(isinstance(other, PrivateNumber)):
raise Exception("Not implemented yet")
entities = self.entities
new_max_val = Counter()
for entity in entities:
new_max_val[entity] = min(self.max_val[entity], other)
return PrivateNumber(min(self.value, other),
new_max_val,
self.min_val)
def hard_sigmoid(self):
return self.min(1).max(0)
def hard_sigmoid_deriv(self):
return ((self < 1) * (self > 0)) + (self < 0) * 0.01 - (self > 1) * 0.01
def __repr__(self):
return str(self.value) + " " + str(self.max_val) + " " + str(self.min_val)
@property
def xmin(self):
return self.min_val.most_common(len(self.min_val))[-1][1]
@property
def xmax(self):
return self.max_val.most_common(1)[0][1]
@property
def entities(self):
return set(self.max_val.keys())
@property
def sensitivity(self):
sens = Counter()
for entity, value in self.max_val.items():
sens[entity] = value - self.min_val[entity]
return sens.most_common()[0][1]
x = PrivateNumber(0.5,Counter({"bob":4, "amos":3}),Counter({"bob":-3, "amos":-1}))
y = PrivateNumber(1.5,Counter({"sue":2}),Counter({"sue":-1}))
z = PrivateNumber(-0.5,Counter({"sue":2}),Counter({"sue":-1}))
In [3]:
a = x + y
In [4]:
b = a * z
In [6]:
b
Out[6]:
In [ ]:
In [2]:
# class PrivacyAccountant():
# def __init__(self, default_budget = 0.1):
# self.entity2epsilon = {}
# self.entity2id = {}
# self.default_budget = default_budget
# def add_entity(self, entity_id, budget=None):
# """Add another entity to the system to be tracked.
# Args:
# entity_id: a string or other unique identifier of the entity
# budget: the epsilon level defining this user's privacy budget
# """
# if(budget is None):
# budget = self.default_budget
# self.entity2id[entity_id] = len(self.entity2id)
# self.entity2epsilon[self.entity2id[entity_id]] = budget
# accountant = PrivacyAccountant()
# class DPTensor():
# def __init__(self, data, entities, max_values=None, min_values=None):
# assert data.shape == entities.shape#[0:-1]
# self.data = data
# self.entities = entities
# if max_values is None:
# max_values = np.inf + np.zeros_like(self.data)
# assert max_values.shape == data.shape
# self.max_values = max_values
# if min_values is None:
# min_values = -np.inf + np.zeros_like(self.data)
# assert min_values.shape == data.shape
# self.min_values = min_values
# def sum(self, dim=0):
# _new_data = self.data.sum(dim)
# return _new_data
# @property
# def sensitivity(self):
# return self.max_values - self.min_values
In [163]:
# results, tags = grid.search("diabetes","#data", verbose=False)
# dataset = results['alice'][0][0:5][:,0:4]
# n_ent = dataset.shape[0]
# n_classes = dataset.shape[1]
# for i in range(n_ent):
# accountant.add_entity("Diabetes Patient #" + str(i))
# d2 = dataset.clone().get()
# entities = th.arange(0,n_ent).view(-1,1).expand(n_ent,n_classes)#.unsqueeze(2)
# db = DPTensor(data=d2,
# entities=entities,
# max_values=d2.max(0)[0].expand(n_ent,n_classes),
# min_values=d2.min(0)[0].expand(n_ent,n_classes))
In [ ]:
In [ ]: