-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathproba_util.py
151 lines (125 loc) · 3.87 KB
/
proba_util.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
from math import factorial as fac
from math import log, ceil, erf, sqrt
def gaussian_center_weight(sigma, t):
""" Weight of the gaussian of std deviation s, on the interval [-t, t]
:param x: (float)
:param y: (float)
:returns: erf( t / (sigma*\sqrt 2) )
"""
return erf(t / (sigma * sqrt(2.)))
def binomial(x, y):
""" Binomial coefficient
:param x: (integer)
:param y: (integer)
:returns: y choose x
"""
try:
binom = fac(x) // fac(y) // fac(x - y)
except ValueError:
binom = 0
return binom
def centered_binomial_pdf(k, x):
""" Probability density function of the centered binomial law of param k at x
:param k: (integer)
:param x: (integer)
:returns: p_k(x)
"""
return binomial(2*k, x+k) / 2.**(2*k)
def build_centered_binomial_law(k):
""" Construct the binomial law as a dictionnary
:param k: (integer)
:param x: (integer)
:returns: A dictionnary {x:p_k(x) for x in {-k..k}}
"""
D = {}
for i in range(-k, k+1):
D[i] = centered_binomial_pdf(k, i)
return D
def mod_switch(x, q, rq):
""" Modulus switching (rounding to a different discretization of the Torus)
:param x: value to round (integer)
:param q: input modulus (integer)
:param rq: output modulus (integer)
"""
return int(round(1.* rq * x / q) % rq)
def mod_centered(x, q):
""" reduction mod q, centered (ie represented in -q/2 .. q/2)
:param x: value to round (integer)
:param q: input modulus (integer)
"""
a = x % q
if a < q/2:
return a
return a - q
def build_mod_switching_error_law(q, rq):
""" Construct Error law: law of the difference introduced by switching from and back a uniform value mod q
:param q: original modulus (integer)
:param rq: intermediate modulus (integer)
"""
D = {}
V = {}
for x in range(q):
y = mod_switch(x, q, rq)
z = mod_switch(y, rq, q)
d = mod_centered(x - z, q)
D[d] = D.get(d, 0) + 1./q
V[y] = V.get(y, 0) + 1
return D
def law_convolution(A, B):
""" Construct the convolution of two laws (sum of independent variables from two input laws)
:param A: first input law (dictionnary)
:param B: second input law (dictionnary)
"""
C = {}
for a in A:
for b in B:
c = a+b
C[c] = C.get(c, 0) + A[a] * B[b]
return C
def law_product(A, B):
""" Construct the law of the product of independent variables from two input laws
:param A: first input law (dictionnary)
:param B: second input law (dictionnary)
"""
C = {}
for a in A:
for b in B:
c = a*b
C[c] = C.get(c, 0) + A[a] * B[b]
return C
def clean_dist(A):
""" Clean a distribution to accelerate further computation (drop element of the support with proba less than 2^-300)
:param A: input law (dictionnary)
"""
B = {}
for (x, y) in A.items():
if y>2**(-300):
B[x] = y
return B
def iter_law_convolution(A, i):
""" compute the -ith forld convolution of a distribution (using double-and-add)
:param A: first input law (dictionnary)
:param i: (integer)
"""
D = {0: 1.0}
i_bin = bin(i)[2:] # binary representation of n
for ch in i_bin:
D = law_convolution(D, D)
D = clean_dist(D)
if ch == '1':
D = law_convolution(D, A)
D = clean_dist(D)
return D
def tail_probability(D, t):
'''
Probability that an drawn from D is strictly greater than t in absolute value
:param D: Law (Dictionnary)
:param t: tail parameter (integer)
'''
s = 0
ma = max(D.keys())
if t >= ma:
return 0
for i in reversed(range(int(ceil(t)), ma)): # Summing in reverse for better numerical precision (assuming tails are decreasing)
s += D.get(i, 0) + D.get(-i, 0)
return s