提交 b8054a90 编辑于 作者: Michael Reneer's avatar Michael Reneer 提交者: tensorflow-copybara
浏览文件

Automated rollback of commit 9fb10c29

PiperOrigin-RevId: 391879106
上级 dcffe1f2
......@@ -10,9 +10,7 @@ py_library(
srcs_version = "PY3",
deps = [
":clipping_factory",
":modular_clipping_factory",
"//tensorflow_federated/python/aggregators:differential_privacy",
"//tensorflow_federated/python/aggregators:secure",
"//tensorflow_federated/python/aggregators:sum_factory",
],
)
......
......@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""The functions for creating the federated computation for hierarchical histogram aggregation."""
import math
import tensorflow as tf
......@@ -98,9 +97,7 @@ def build_hierarchical_histogram_computation(
clip_mechanism: str = 'sub-sampling',
max_records_per_user: int = 10,
dp_mechanism: str = 'no-noise',
noise_multiplier: float = 0.0,
expected_clients_per_round: int = 10,
bits: int = 22):
noise_multiplier: float = 0.0):
"""Creates the TFF computation for hierarchical histogram aggregation.
Args:
......@@ -120,28 +117,8 @@ def build_hierarchical_histogram_computation(
use. Currently supported mechanisms are
- 'no-noise': (Default) Tree aggregation mechanism without noise.
- 'central-gaussian': Tree aggregation with central Gaussian mechanism.
- 'distributed-discrete-gaussian': Tree aggregation mechanism with
the distributed discrete Gaussian mechanism in "The Distributed Discrete
Gaussian Mechanism for Federated Learning with Secure Aggregation. Peter
Kairouz, Ziyu Liu, Thomas Steinke".
noise_multiplier: A `float` specifying the noise multiplier (central noise
stddev / L2 clip norm) for model updates. Defaults to 0.0.
expected_clients_per_round: An `int` specifying the lower bound on the
expected number of clients. Only needed when `dp_mechanism` is
'distributed-discrete-gaussian'. Defaults to 10.
bits: A positive integer specifying the communication bit-width B (where
2**B will be the field size for SecAgg operations). Only needed when
`dp_mechanism` is 'distributed-discrete-gaussian'. Please read the below
precautions carefully and set `bits` accordingly. Otherwise, unexpected
overflow or accuracy degradation might happen.
(1) Should be in the inclusive range [1, 22] to avoid overflow inside
secure aggregation;
(2) Should be at least as large as
`log2(4 * sqrt(expected_clients_per_round)* noise_multiplier *
l2_norm_bound + expected_clients_per_round * max_records_per_user) + 1`
to avoid accuracy degradation caused by frequent modular clipping;
(3) If the number of clients exceed `expected_clients_per_round`, overflow
might happen.
Returns:
A federated computation that performs hierarchical histogram aggregation.
......@@ -156,10 +133,6 @@ def build_hierarchical_histogram_computation(
'max_records_per_user')
_check_membership(dp_mechanism, hihi_factory.DP_MECHANISMS, 'dp_mechanism')
_check_greater_than_equal_thres(noise_multiplier, 0., noise_multiplier)
_check_positive(expected_clients_per_round, 'expected_clients_per_round')
_check_in_range(bits, 'bits', 1, 22)
_check_greater_than_equal_thres(bits, math.log2(expected_clients_per_round),
'bits')
@computations.tf_computation(computation_types.SequenceType(tf.float32))
def client_work(client_data):
......@@ -168,7 +141,7 @@ def build_hierarchical_histogram_computation(
agg_factory = hihi_factory.create_hierarchical_histogram_aggregation_factory(
num_bins, arity, clip_mechanism, max_records_per_user, dp_mechanism,
noise_multiplier, expected_clients_per_round, bits)
noise_multiplier)
process = agg_factory.create(client_work.type_signature.result)
@computations.federated_computation(
......@@ -207,10 +180,3 @@ def _check_membership(value, valid_set, label):
if value not in valid_set:
raise ValueError(f'`{label}` must be one of {valid_set}. '
f'Found {value}.')
def _check_in_range(value, label, left, right):
"""Checks that a scalar value is in specified range."""
if not value >= left or not value <= right:
raise ValueError(f'{label} should be within [{left}, {right}]. '
f'Found {value}.')
......@@ -17,10 +17,8 @@ import math
import tensorflow_privacy as tfp
from tensorflow_federated.python.aggregators import differential_privacy
from tensorflow_federated.python.aggregators import secure
from tensorflow_federated.python.aggregators import sum_factory
from tensorflow_federated.python.analytics.hierarchical_histogram import clipping_factory
from tensorflow_federated.python.analytics.hierarchical_histogram import modular_clipping_factory
# Supported no-noise mechanisms.
NO_NOISE_MECHANISMS = ['no-noise']
......@@ -30,12 +28,7 @@ CENTRAL_DP_MECHANISMS = [
'central-gaussian', # Central Gaussian mechanism.
]
# Supported distributed DP mechanisms.
DISTRIBUTED_DP_MECHANISMS = [
'distributed-discrete-gaussian', # Distributed discrete Gaussian mechanism.
]
DP_MECHANISMS = CENTRAL_DP_MECHANISMS + DISTRIBUTED_DP_MECHANISMS + NO_NOISE_MECHANISMS
DP_MECHANISMS = CENTRAL_DP_MECHANISMS + NO_NOISE_MECHANISMS
def create_hierarchical_histogram_aggregation_factory(
......@@ -43,10 +36,8 @@ def create_hierarchical_histogram_aggregation_factory(
arity: int = 2,
clip_mechanism: str = 'sub-sampling',
max_records_per_user: int = 10,
dp_mechanism: str = 'no-noise',
noise_multiplier: float = 0.0,
expected_clients_per_round: int = 10,
bits: int = 22):
dp_mechanism: str = 'central-gaussian',
noise_multiplier: float = 0.0):
"""Creates hierarchical histogram aggregation factory.
Hierarchical histogram factory is constructed by composing 3 aggregation
......@@ -74,31 +65,12 @@ def create_hierarchical_histogram_aggregation_factory(
can include in their local histogram. Defaults to 10.
dp_mechanism: A `str` representing the differentially private mechanism to
use. Currently supported mechanisms are
- 'no-noise': (Default) Tree aggregation mechanism without noise.
- 'central-gaussian': Tree aggregation with central Gaussian mechanism.
- 'distributed-discrete-gaussian': Tree aggregation mechanism with
distributed discrete Gaussian mechanism in "The Distributed Discrete
Gaussian Mechanism for Federated Learning with Secure Aggregation. Peter
Kairouz, Ziyu Liu, Thomas Steinke".
- 'central-gaussian': (Default) Tree aggregation with central Gaussian
mechanism.
- 'no-noise': Tree aggregation mechanism without noise.
noise_multiplier: A `float` specifying the noise multiplier (central noise
stddev / L2 clip norm) for model updates. Only needed when `dp_mechanism`
is not 'no-noise'. Defaults to 0.0.
expected_clients_per_round: An `int` specifying the lower bound of the
expected number of clients. Only needed when `dp_mechanism` is
'distributed-discrete-gaussian. Defaults to 10.
bits: A positive integer specifying the communication bit-width B (where
2**B will be the field size for SecAgg operations). Only needed when
`dp_mechanism` is 'distributed-discrete-gaussian'. Please read the below
precautions carefully and set `bits` accordingly. Otherwise, unexpected
overflow or accuracy degradation might happen.
(1) Should be in the inclusive range [1, 22] to avoid overflow inside
secure aggregation;
(2) Should be at least as large as
`log2(4 * sqrt(expected_clients_per_round)* noise_multiplier *
l2_norm_bound + expected_clients_per_round * max_records_per_user) + 1`
to avoid accuracy degradation caused by frequent modular clipping;
(3) If the number of clients exceed `expected_clients_per_round`, overflow
might happen.
Returns:
`tff.aggregators.UnweightedAggregationFactory`.
......@@ -114,12 +86,15 @@ def create_hierarchical_histogram_aggregation_factory(
_check_positive(max_records_per_user, 'max_records_per_user')
_check_membership(dp_mechanism, DP_MECHANISMS, 'dp_mechanism')
_check_non_negative(noise_multiplier, 'noise_multiplier')
_check_positive(expected_clients_per_round, 'expected_clients_per_round')
_check_in_range(bits, 'bits', 1, 22)
# Converts `max_records_per_user` to the corresponding norm bound according to
# the chosen `clip_mechanism` and `dp_mechanism`.
if dp_mechanism in ['central-gaussian', 'distributed-discrete-gaussian']:
# Build nested aggregtion factory from innermost to outermost.
# 1. Sum factory. The most inner factory that sums the preprocessed records.
nested_factory = sum_factory.SumFactory()
# 2. DP operations.
# (1) Converts `max_records_per_user` to the corresponding norm bound
# according to the chosen `clip_mechanism` and `dp_mechanism`.
if dp_mechanism in ['central-gaussian']:
if clip_mechanism == 'sub-sampling':
l2_norm_bound = max_records_per_user * math.sqrt(
_tree_depth(num_bins, arity))
......@@ -139,82 +114,14 @@ def create_hierarchical_histogram_aggregation_factory(
square_layer_l2_norm_bound *= arity
l2_norm_bound = math.sqrt(square_l2_norm_bound)
# Build nested aggregtion factory from innermost to outermost.
# 1. Sum factory. The most inner factory that sums the preprocessed records.
# (1) If `dp_mechanism` is in `CENTRAL_DP_MECHANISMS` or
# `NO_NOISE_MECHANISMS`, should be `SumFactory`.
if dp_mechanism in CENTRAL_DP_MECHANISMS + NO_NOISE_MECHANISMS:
nested_factory = sum_factory.SumFactory()
# (2) If `dp_mechanism` is in `DISTRIBUTED_DP_MECHANISMS`, should be
# `SecureSumFactory`. To preserve DP and avoid overflow, we have 4 modular
# clips from nesting two modular clip aggregators:
# #1. outer-client: clips to [-2**(bits-1), 2**(bits-1))
# Bounds the client values.
# #2. inner-client: clips to [0, 2**bits)
# Similar to applying a two's complement to the values such that
# frequent values (post-rotation) are now near 0 (representing small
# positives) and 2**bits (small negatives). 0 also always map to 0, and
# we do not require another explicit value range shift from
# [-2**(bits-1), 2**(bits-1)] to [0, 2**bits] to make sure that values
# are compatible with SecAgg's mod m = 2**bits. This can be reverted at
# #4.
# #3. inner-server: clips to [0, 2**bits)
# Ensures the aggregated value range does not grow by
# `log2(expected_clients_per_round)`.
# NOTE: If underlying SecAgg is implemented using the new
# `tff.federated_secure_modular_sum()` operator with the same
# modular clipping range, then this would correspond to a no-op.
# #4. outer-server: clips to [-2**(bits-1), 2**(bits-1))
# Keeps aggregated values centered near 0 out of the logical SecAgg
# black box for outer aggregators.
elif dp_mechanism in DISTRIBUTED_DP_MECHANISMS:
# TODO(b/196312838): Please add scaling to the distributed case once we have
# a stable guideline for setting scaling factor to improve performance and
# avoid overflow. The below test is to make sure that modular clipping
# happens with small probability so the accuracy of the result won't be
# harmed. However, if the number of clients exceeds
# `expected_clients_per_round`, overflow still might happen. It is the
# caller's responsibility to carefully choose `bits` according to system
# details to avoid overflow or performance degradation.
if bits < math.log2(4 * math.sqrt(expected_clients_per_round) *
noise_multiplier * l2_norm_bound +
expected_clients_per_round * max_records_per_user) + 1:
raise ValueError(f'The selected bit-width ({bits}) is too small for the '
f'given parameters (expected_clients_per_round = '
f'{expected_clients_per_round}, max_records_per_user = '
f'{max_records_per_user}, noise_multiplier = '
f'{noise_multiplier}) and will harm the accuracy of the '
f'result. Please decrease the '
f'`expected_clients_per_round` / `max_records_per_user` '
f'/ `noise_multiplier`, or increase `bits`.')
nested_factory = secure.SecureSumFactory(
upper_bound_threshold=2**bits - 1, lower_bound_threshold=0)
nested_factory = modular_clipping_factory.ModularClippingSumFactory(
clip_range_lower=0,
clip_range_upper=2**bits,
inner_agg_factory=nested_factory)
nested_factory = modular_clipping_factory.ModularClippingSumFactory(
clip_range_lower=-2**(bits - 1),
clip_range_upper=2**(bits - 1),
inner_agg_factory=nested_factory)
# 2. DP operations.
# Constructs `DifferentiallyPrivateFactory` according to the chosen
# `dp_mechanism`.
# (2) Constructs `DifferentiallyPrivateFactory` according to the chosen
# `dp_mechanism`.
if dp_mechanism == 'central-gaussian':
query = tfp.privacy.dp_query.tree_aggregation_query.TreeRangeSumQuery.build_central_gaussian_query(
l2_norm_bound, noise_multiplier * l2_norm_bound, arity)
# If the inner `DifferentiallyPrivateFactory` uses `GaussianSumQuery`, then
# the record is casted to `tf.float32` before feeding to the DP factory.
cast_to_float = True
elif dp_mechanism == 'distributed-discrete-gaussian':
query = tfp.privacy.dp_query.tree_aggregation_query.TreeRangeSumQuery.build_distributed_discrete_gaussian_query(
l2_norm_bound, noise_multiplier * l2_norm_bound /
math.sqrt(expected_clients_per_round), arity)
# If the inner `DifferentiallyPrivateFactory` uses
# `DistributedDiscreteGaussianQuery`, then the record is kept as `tf.int32`
# before feeding to the DP factory.
cast_to_float = False
elif dp_mechanism == 'no-noise':
inner_query = tfp.privacy.dp_query.no_privacy_query.NoPrivacySumQuery()
query = tfp.privacy.dp_query.tree_aggregation_query.TreeRangeSumQuery(
......@@ -258,13 +165,6 @@ def _check_membership(value, valid_set, label):
f'Found {value}.')
def _check_in_range(value, label, left, right):
"""Checks that a scalar value is in specified range."""
if not value >= left or not value <= right:
raise ValueError(f'{label} should be within [{left}, {right}]. '
f'Found {value}.')
def _tree_depth(num_leaves: int, arity: int):
"""Returns the depth of the tree given the number of leaf nodes and arity."""
return math.ceil(math.log(num_leaves) / math.log(arity)) + 1
......@@ -147,105 +147,27 @@ class TreeAggregationFactoryComputationTest(test_case.TestCase,
self.assertTrue(
process.next.type_signature.is_equivalent_to(expected_next_type))
@parameterized.named_parameters(
('test_1_2_sub_sampling', 1, 2, 'sub-sampling'),
('test_5_3_sub_sampling', 5, 3, 'sub-sampling'),
('test_3_2_distinct', 3, 2, 'distinct'),
('test_2_3_distinct', 2, 3, 'distinct'),
)
def test_distributed_discrete_gaussian_tree_aggregation(
self, value_shape, arity, clip_mechanism):
agg_factory = hihi_factory.create_hierarchical_histogram_aggregation_factory(
num_bins=value_shape,
arity=arity,
clip_mechanism=clip_mechanism,
dp_mechanism='distributed-discrete-gaussian',
)
self.assertIsInstance(agg_factory, factory.UnweightedAggregationFactory)
value_type = computation_types.to_type((tf.int32, (value_shape,)))
process = agg_factory.create(value_type)
self.assertIsInstance(process, aggregation_process.AggregationProcess)
query = tfp.privacy.dp_query.tree_aggregation_query.TreeRangeSumQuery(
arity=arity,
inner_query=tfp.privacy.dp_query.distributed_discrete_gaussian_query
.DistributedDiscreteGaussianSumQuery(
l2_norm_bound=1.0, local_stddev=1.0))
query_state = query.initial_global_state()
query_state_type = type_conversions.type_from_tensors(query_state)
query_metrics_type = type_conversions.type_from_tensors(
query.derive_metrics(query_state))
server_state_type = computation_types.at_server((query_state_type, ()))
expected_initialize_type = computation_types.FunctionType(
parameter=None, result=server_state_type)
self.assertTrue(
process.initialize.type_signature.is_equivalent_to(
expected_initialize_type))
expected_measurements_type = computation_types.at_server(
collections.OrderedDict(
dp_query_metrics=query_metrics_type,
dp=(collections.OrderedDict(
modclip=collections.OrderedDict(
modclip=collections.OrderedDict(
secure_upper_clipped_count=tf.int32,
secure_lower_clipped_count=tf.int32,
secure_upper_threshold=tf.int32,
secure_lower_threshold=tf.int32))))))
tree_depth = hihi_factory._tree_depth(value_shape, arity)
flat_tree_shape = (arity**tree_depth - 1) // (arity - 1)
result_value_type = computation_types.to_type(
collections.OrderedDict([
('flat_values',
computation_types.to_type((tf.int32, (flat_tree_shape,)))),
('nested_row_splits', [(tf.int64, (tree_depth + 1,))])
]))
value_type = computation_types.to_type((tf.int32, (value_shape,)))
expected_next_type = computation_types.FunctionType(
parameter=collections.OrderedDict(
state=server_state_type,
value=computation_types.at_clients(value_type)),
result=measured_process.MeasuredProcessOutput(
state=server_state_type,
result=computation_types.at_server(result_value_type),
measurements=expected_measurements_type))
self.assertTrue(
process.next.type_signature.is_equivalent_to(expected_next_type))
class TreeAggregationFactoryExecutionTest(test_case.TestCase,
parameterized.TestCase):
@parameterized.named_parameters(
('non_positive_value_shape', 0, 2, 'sub-sampling', 1, 'central-gaussian',
1., 1, 1),
('invalid_arity', 1, 1, 'sub-sampling', 1, 'central-gaussian', 1., 1, 1),
('invalid_clip_mechanism', 1, 2, 'invalid', 1, 'central-gaussian', 1., 1,
1),
1.),
('invalid_arity', 1, 1, 'sub-sampling', 1, 'central-gaussian', 1.),
('invalid_clip_mechanism', 1, 2, 'invalid', 1, 'central-gaussian', 1.),
('non_positive_max_records_per_user', 1, 2, 'sub-sampling', 0,
'central-gaussian', 1., 1, 1),
('invalid_dp_mechanism', 1, 2, 'sub-sampling', 1, 'invalid', 1., 1, 1),
'central-gaussian', 1.),
('invalid_dp_mechanism', 1, 2, 'sub-sampling', 1, 'invalid', 1.),
('negative_noise_multiplier', 1, 2, 'sub-sampling', 1, 'central-gaussian',
-1., 1, 1),
('non_positive_expected_clients_per_round', 1, 2, 'sub-sampling', 1,
'central-gaussian', 1., 0, 1),
('bits_less_than_1', 1, 2, 'sub-sampling', 1, 'central-gaussian', 1., 1,
0),
('bits_greater_than_22', 1, 2, 'sub-sampling', 1, 'central-gaussian', 1.,
1, 23),
('bits_less_than_lower_bound', 1, 2, 'sub-sampling', 1,
'distributed-discrete-gaussian', 4., 8, 4),
-1.),
)
def test_raises_error(self, value_shape, arity, clip_mechanism,
max_records_per_user, dp_mechanism, noise_multiplier,
expected_clients_per_round, bits):
max_records_per_user, dp_mechanism, noise_multiplier):
with self.assertRaises(ValueError):
hihi_factory.create_hierarchical_histogram_aggregation_factory(
value_shape, arity, clip_mechanism, max_records_per_user,
dp_mechanism, noise_multiplier, expected_clients_per_round, bits)
dp_mechanism, noise_multiplier)
@parameterized.named_parameters(
('test_1_1_2_sub_sampling', 1, 1, 2, 'sub-sampling'),
......@@ -407,117 +329,6 @@ class TreeAggregationFactoryExecutionTest(test_case.TestCase,
expected_l1_norm,
atol=300. * np.sqrt(arity**layer) * noise_multiplier)
@parameterized.named_parameters(
('test_1_1_2_sub_sampling', 1, 1, 2, 'sub-sampling', 0.1),
('test_2_3_3_sub_sampling', 2, 3, 3, 'sub-sampling', 1.0),
('test_3_5_2_distinct', 3, 5, 2, 'distinct', 5.0),
('test_5_3_3_distinct', 5, 3, 3, 'distinct', 10.0),
)
def test_distributed_discrete_gaussian_tree_aggregation_wo_clip(
self, value_shape, num_clients, arity, clip_mechanism, noise_multiplier):
client_records = []
for _ in range(num_clients):
client_records.append(np.arange(value_shape, dtype=int).tolist())
agg_factory = hihi_factory.create_hierarchical_histogram_aggregation_factory(
num_bins=value_shape,
arity=arity,
clip_mechanism=clip_mechanism,
max_records_per_user=5,
dp_mechanism='distributed-discrete-gaussian',
noise_multiplier=noise_multiplier)
value_type = computation_types.to_type((tf.int32, (value_shape,)))
process = agg_factory.create(value_type)
state = process.initialize()
output = process.next(state, client_records).result
if clip_mechanism == 'sub-sampling':
reference_aggregated_record = build_tree_from_leaf.create_hierarchical_histogram(
np.sum(client_records, axis=0).astype(int).tolist(), arity)
else:
reference_aggregated_record = build_tree_from_leaf.create_hierarchical_histogram(
np.sum(np.minimum(client_records, 1), axis=0).astype(int).tolist(),
arity)
# 300 is a rough estimation of six-sigma considering the effect of the L2
# norm bound and the privacy composition.
self.assertAllClose(
output, reference_aggregated_record, atol=300. * noise_multiplier)
@parameterized.named_parameters(
('test_1_1_2_sub_sampling', 1, 1, 2, 'sub-sampling', 1, 0.1),
('test_2_3_3_sub_sampling', 2, 3, 3, 'sub-sampling', 2, 1.0),
('test_3_5_2_distinct', 3, 5, 2, 'distinct', 3, 5.0),
('test_5_3_3_distinct', 5, 3, 3, 'distinct', 2, 10.0),
)
def test_distributed_discrete_gaussian_tree_aggregation_w_clip(
self, value_shape, num_clients, arity, clip_mechanism,
max_records_per_user, noise_multiplier):
client_records = []
for _ in range(num_clients):
client_records.append(np.arange(value_shape, dtype=int).tolist())
agg_factory = hihi_factory.create_hierarchical_histogram_aggregation_factory(
num_bins=value_shape,
arity=arity,
clip_mechanism=clip_mechanism,
max_records_per_user=max_records_per_user,
dp_mechanism='distributed-discrete-gaussian',
noise_multiplier=noise_multiplier)
value_type = computation_types.to_type((tf.int32, (value_shape,)))
process = agg_factory.create(value_type)
state = process.initialize()
output = process.next(state, client_records).result
if clip_mechanism == 'sub-sampling':
expected_l1_norm = np.sum([
min(np.linalg.norm(x, ord=1), max_records_per_user)
for x in client_records
])
elif clip_mechanism == 'distinct':
expected_l1_norm = np.sum([
min(np.linalg.norm(x, ord=0), max_records_per_user)
for x in client_records
])
# 300 is a rough estimation of six-sigma considering the effect of the L2
# norm bound and the privacy composition.
for layer in range(hihi_factory._tree_depth(value_shape, arity)):
self.assertAllClose(
tf.math.reduce_sum(output[layer]),
expected_l1_norm,
atol=300. * np.sqrt(arity**layer) * noise_multiplier)
@parameterized.named_parameters(
('test_1_3_2', 1, 3, 2),
('test_2_4_3', 2, 4, 3),
)
def test_distributed_discrete_gaussian_tree_aggregation_no_overflow(
self, value_shape, num_clients, arity):
client_records = []
for _ in range(num_clients):
client_records.append(np.zeros(value_shape, dtype=int).tolist())
agg_factory = hihi_factory.create_hierarchical_histogram_aggregation_factory(
num_bins=value_shape,
arity=arity,
clip_mechanism='sub-sampling',
max_records_per_user=1,
dp_mechanism='distributed-discrete-gaussian',
noise_multiplier=0)
value_type = computation_types.to_type((tf.int32, (value_shape,)))
process = agg_factory.create(value_type)
state = process.initialize()
output = process.next(state, client_records).result
reference_aggregated_record = build_tree_from_leaf.create_hierarchical_histogram(
np.zeros_like(client_records[0], dtype=int).tolist(), arity)
self.assertAllClose(output, reference_aggregated_record)
if __name__ == '__main__':
execution_contexts.set_test_execution_context()
......
......@@ -51,32 +51,21 @@ class ClientWorkTest(test_case.TestCase, parameterized.TestCase):
class HierarchicalHistogramTest(test_case.TestCase, parameterized.TestCase):
@parameterized.named_parameters(
('data_range_error', [2, 1], 1, 2, 'sub-sampling', 1, 'central-gaussian',
0.1, 1, 1),
('num_bins_error', [1, 2], 0, 2, 'sub-sampling', 1, 'central-gaussian',
0.1, 1, 1),
('arity_error', [1, 2], 1, 1, 'sub-sampling', 1, 'central-gaussian', 0.1,
1, 1),
('clip_mechanism_error', [1, 2], 1, 2, 'invalid', 1, 'central-gaussian',
0.1, 1, 1),
('data_range_error', [2, 1
], 1, 2, 'sub-sampling', 1, 'central-gaussian', 0.1),
('num_bins_error', [1, 2
], 0, 2, 'sub-sampling', 1, 'central-gaussian', 0.1),
('arity_error', [1, 2], 1, 1, 'sub-sampling', 1, 'central-gaussian', 0.1),
('clip_mechanism_error', [1, 2
], 1, 2, 'invalid', 1, 'central-gaussian', 0.1),
('max_records_per_user_error', [1, 2], 1, 2, 'sub-sampling', 0,
'central-gaussian', 0.1, 1, 1),
('dp_mechanism_error', [1, 2
], 1, 2, 'sub-sampling', 1, 'invalid', 0.1, 1, 1),
'central-gaussian', 0.1),
('dp_mechanism_error', [1, 2], 1, 2, 'sub-sampling', 1, 'invalid', 0.1),
('noise_multiplier_error', [1, 2], 1, 2, 'sub-sampling', 1,
'central-gaussian', -0.1, 1, 1),
('expected_clients_per_round_error', [1, 2], 1, 2, 'sub-sampling', 1,
'central-gaussian', 0.1, 0, 1),
('bits_less_than_1', [1, 2], 1, 2, 'sub-sampling', 1, 'central-gaussian',
0.1, 1, 0),
('bits_large_than_23', [1, 2], 1, 2, 'sub-sampling', 1,
'central-gaussian', 0.1, 1, 23),
('bits_less_than_log_client_num', [1, 2], 1, 2, 'sub-sampling', 1,
'central-gaussian', 0.1, 8, 2),
'central-gaussian', -0.1),
)
def test_raises_error(self, data_range, num_bins, arity, clip_mechanism,