blob: dbe283fb364017878fb2e23f424730c64a71cd02 [file] [log] [blame]
# Copyright 2024 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper functions for Renyi Tester."""
from typing import Optional
import numpy as np
from dp_auditorium.configs import privacy_property
from dp_auditorium.configs import property_tester_config
def split_train_test_samples(
samples: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]:
"""Splits a sample into training and testing samples.
Args:
samples: Original sample. Assumes that the first dimension in the array is
the number of examples.
Returns:
Two samples of the same rank as samples but with the first and second
half of the data respectively. When the number of samples has an odd length,
the first half will have one fewer record.
Raises:
ValueError if samples has less than 2 elements.
"""
n = samples.shape[0]
if n == 1:
raise ValueError(
'Must have at least 2 elements in sample to generate '
'training and test samples'
)
return samples[0 : n // 2, ...], samples[n // 2 :, ...]
def validate_training_config(
training_config: property_tester_config.TrainingConfig,
):
"""Returns true if the training_params parameters are valid."""
if (
not training_config.optimizer_learning_rate
or training_config.optimizer_learning_rate <= 0
):
raise ValueError('Learning rate should be positive.')
if (
not training_config.training_epochs
or training_config.training_epochs <= 0
):
raise ValueError('Training epochs should be positive.')
if not training_config.batch_size or training_config.batch_size <= 0:
raise ValueError('Batch size should be positive.')
if (
not training_config.model_output_coordinate_bound
or training_config.model_output_coordinate_bound <= 0
):
raise ValueError('Model output coordinate bound should be positive.')
def _validate_epsilon_parameter(epsilon: Optional[float]):
"""Checks that the epsilon parameter is valid."""
if not epsilon:
raise ValueError('`epsilon` should be specified.')
if epsilon <= 0:
raise ValueError('`epsilon` should be positive.')
def validate_approximate_dp_property(
approximate_dp: privacy_property.ApproximateDp,
):
"""Checks that the approximate differential privacy parameters are valid.
Args:
approximate_dp: An approximate differential privacy configuration.
Raises:
ValueError: epsilon is None or non-positive.
ValueError: delta is None or negative.
"""
_validate_epsilon_parameter(approximate_dp.epsilon)
if approximate_dp.delta is None:
raise ValueError('`delta` should be specified for approximate DP.')
if approximate_dp.delta < 0:
raise ValueError('`delta` should be non-negative.')
def validate_pure_dp_property(pure_dp: privacy_property.PureDp):
"""Checks that the pure differential privacy parameters are valid.
Args:
pure_dp: A pure differential privacy configuration.
Raises:
ValueError: epsilon is None or non-positive.
"""
_validate_epsilon_parameter(pure_dp.epsilon)
def validate_renyi_dp_property(renyi_dp: privacy_property.RenyiDp):
"""Checks that Renyi differential privacy parameters are valid.
Args:
renyi_dp: A pure differential privacy configuration.
Raises:
ValueError: epsilon is None or non-positive.
ValueError: alpha is None or less or equal than one.
"""
_validate_epsilon_parameter(renyi_dp.epsilon)
if not renyi_dp.alpha:
raise ValueError(
'The order Renyi divergence `alpha` should be specified for Renyi DP.'
)
if renyi_dp.alpha <= 1:
raise ValueError(
'The order of Renyi divergence `alpha` should be greater than one.'
)