def make_randomized_response(categories: set[T], prob: f64):
input_domain = AtomDomain(bool)
input_metric = DiscreteMetric()
output_measure = MaxDivergence()
categories = list(categories)
if len(categories) < 2: raise ValueError("expected at least two categories")
num_categories = len(categories)
if not (1 / num_categories <= prob <= 1): raise ValueError("probability must be within [1/num_categories, 1]")
if prob == 1.0:
c = float("inf")
else:
c = p.inf_div((1).neg_inf_sub(prob)) \
.inf_mul(num_categories.inf_sub(1)) \
.inf_ln()
def privacy_map(d_in: u32) -> QO:
if d_in == 0:
return 0
else:
return c
def function(truth: bool) -> bool: index = categories.index(truth)
sample = usize.sample_uniform_int_below(
len(num_categories) - (0 if index == -1 else 1))
if index != -1 and sample >= index:
sample += 1
lie = categories[sample]
be_honest = sample_bernoulli_float(prob, false)
is_member = index != -1
return truth if be_honest and is_member else lie
return Measurement(input_domain, function, input_metric, output_measure, privacy_map)