-
Notifications
You must be signed in to change notification settings - Fork 200
/
utils.py
198 lines (146 loc) · 6.93 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# MIT License
#
# Copyright (C) IBM Corporation 2019
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""
Basic functions and other utilities for the differential privacy library
"""
import secrets
import warnings
import numpy as np
from sklearn.utils import check_random_state as skl_check_random_state
def copy_docstring(source):
"""Decorator function to copy a docstring from a `source` function to a `target` function.
The docstring is only copied if a docstring is present in `source`, and if none is present in `target`. Takes
inspiration from similar in `matplotlib`.
Parameters
----------
source : method
Source function from which to copy the docstring. If ``source.__doc__`` is empty, do nothing.
Returns
-------
target : method
Target function with new docstring.
"""
def copy_func(target):
if source.__doc__ and not target.__doc__:
target.__doc__ = source.__doc__
return target
return copy_func
def warn_unused_args(args):
"""Warn the user about supplying unused `args` to a diffprivlib model.
Arguments can be supplied as a string, a list of strings, or a dictionary as supplied to kwargs.
Parameters
----------
args : str or list or dict
Arguments for which warnings should be thrown.
Returns
-------
None
"""
if isinstance(args, str):
args = [args]
for arg in args:
warnings.warn(f"Parameter '{arg}' is not functional in diffprivlib. Remove this parameter to suppress this "
"warning.", DiffprivlibCompatibilityWarning)
def check_random_state(seed, secure=False):
"""Turn seed into a np.random.RandomState or secrets.SystemRandom instance.
If secure=True, and seed is None (or was generated from a previous None seed), then secrets is used. Otherwise a
np.random.RandomState is used.
Parameters
----------
seed : None, int or instance of RandomState
If seed is None and secure is False, return the RandomState singleton used by np.random.
If seed is None and secure is True, return a SystemRandom instance from secrets.
If seed is an int, return a new RandomState instance seeded with seed.
If seed is already a RandomState or SystemRandom instance, return it.
Otherwise raise ValueError.
secure : bool, default: False
Specifies if a secure random number generator from secrets can be used.
"""
if secure:
if isinstance(seed, secrets.SystemRandom):
return seed
if seed is None or seed is np.random.mtrand._rand: # pylint: disable=protected-access
return secrets.SystemRandom()
elif isinstance(seed, secrets.SystemRandom):
raise ValueError("secrets.SystemRandom instance cannot be passed when secure is False.")
return skl_check_random_state(seed)
class Budget(tuple):
"""Custom tuple subclass for privacy budgets of the form (epsilon, delta).
The ``Budget`` class allows for correct comparison/ordering of privacy budget, ensuring that both epsilon and delta
satisfy the comparison (tuples are compared lexicographically). Additionally, tuples are represented with added
verbosity, labelling epsilon and delta appropriately.
Examples
--------
>>> from diffprivlib.utils import Budget
>>> Budget(1, 0.5)
(epsilon=1, delta=0.5)
>>> Budget(2, 0) >= Budget(1, 0.5)
False
>>> (2, 0) >= (1, 0.5) # Tuples are compared with lexicographic ordering
True
"""
def __new__(cls, epsilon, delta):
if epsilon < 0:
raise ValueError("Epsilon must be non-negative")
if not 0 <= delta <= 1:
raise ValueError("Delta must be in [0, 1]")
return tuple.__new__(cls, (epsilon, delta))
def __gt__(self, other):
if self.__ge__(other) and not self.__eq__(other):
return True
return False
def __ge__(self, other):
if self[0] >= other[0] and self[1] >= other[1]:
return True
return False
def __lt__(self, other):
if self.__le__(other) and not self.__eq__(other):
return True
return False
def __le__(self, other):
if self[0] <= other[0] and self[1] <= other[1]:
return True
return False
def __repr__(self):
return f"(epsilon={self[0]}, delta={self[1]})"
class BudgetError(ValueError):
"""Custom exception to capture the privacy budget being exceeded, typically controlled by a
:class:`.BudgetAccountant`.
For example, this exception may be raised when the user:
- Attempts to execute a query which would exceed the privacy budget of the accountant.
- Attempts to change the slack of the accountant in such a way that the existing budget spends would exceed the
accountant's budget.
"""
class PrivacyLeakWarning(RuntimeWarning):
"""Custom warning to capture privacy leaks resulting from incorrect parameter setting.
For example, this warning may occur when the user:
- fails to specify the bounds or range of data to a model where required (e.g., `bounds=None` to
:class:`.GaussianNB`).
- inputs data to a model that falls outside the bounds or range originally specified.
"""
class DiffprivlibCompatibilityWarning(RuntimeWarning):
"""Custom warning to capture inherited class arguments that are not compatible with diffprivlib.
The purpose of the warning is to alert the user of the incompatibility, but to continue execution having fixed the
incompatibility at runtime.
For example, this warning may occur when the user:
- passes a parameter value that is not compatible with diffprivlib (e.g., `solver='liblinear'` to
:class:`.LogisticRegression`)
- specifies a non-default value for a parameter that is ignored by diffprivlib (e.g., `intercept_scaling=0.5`
to :class:`.LogisticRegression`.
"""
warnings.simplefilter('always', PrivacyLeakWarning)