File sball.py changed (mode: 100644) (index bac994a..e9ebd21) |
2 |
2 |
# coding: utf-8 |
# coding: utf-8 |
3 |
3 |
|
|
4 |
4 |
import numpy as np |
import numpy as np |
5 |
|
#from scipy import stats |
|
6 |
|
from scipy import special # for S_ball |
|
7 |
|
from scipy import integrate # for S_ball |
|
|
5 |
|
import scipy.special as sc |
8 |
6 |
|
|
9 |
|
# нельзя просто так взять и написать Ньютонову методу |
|
10 |
|
# fails on nvar = 501, fails on Sball(500).get_r(0), fails on Sball(800).get_r(0.999) |
|
11 |
7 |
|
|
12 |
|
class Sball: |
|
|
8 |
|
####################################################### |
|
9 |
|
# s-balls -- tools to calc probabilities and radii ### |
|
10 |
|
####################################################### |
|
11 |
|
|
|
12 |
|
|
|
13 |
|
def get_ps_ball(d,R): |
|
14 |
|
"returns probability of falling inside d-ball with radius R" |
|
15 |
|
#return np.sum(np.exp(-(rho**2)/2)*rho**(d-1) )* R/n |
|
16 |
|
return sc.gammainc(d/2, R**2/2) |
|
17 |
|
|
|
18 |
|
def get_pf_ball(d,R): |
|
19 |
|
"returns probability of falling outside d-ball with radius R" |
|
20 |
|
return sc.gammaincc(d/2, R**2/2) |
|
21 |
|
|
|
22 |
|
def get_Radius_pf(d,pf_ball): |
|
23 |
|
"returns radius of an d-ball with probability pf outside" |
|
24 |
|
rsqdiv2 = sc.gammainccinv(d/2, pf_ball) |
|
25 |
|
return np.sqrt(2*rsqdiv2) #radius |
|
26 |
|
|
|
27 |
|
def get_Radius_ps(d,ps_ball): |
|
28 |
|
"returns radius of an d-ball with probability ps inside" |
|
29 |
|
rsqdiv2 = sc.gammaincinv(d/2, ps_ball) |
|
30 |
|
return np.sqrt(2*rsqdiv2) #radius |
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
|
34 |
|
|
|
35 |
|
|
|
36 |
|
# implement class compatible to the old ones |
|
37 |
|
|
|
38 |
|
# dispatcher |
|
39 |
|
def Sball(nvar): |
|
40 |
|
if nvar == 2: |
|
41 |
|
return Sball_2D(nvar) |
|
42 |
|
else: |
|
43 |
|
return Sball_nD(nvar) |
|
44 |
|
|
|
45 |
|
class Sball_nD: |
13 |
46 |
def __init__(self, nvar): |
def __init__(self, nvar): |
14 |
47 |
self.nvar = nvar |
self.nvar = nvar |
15 |
|
if nvar != 2: |
|
16 |
|
self.C = 2**(1-nvar/2) / special.gamma(nvar/2) |
|
17 |
|
self.logC = (1-nvar/2)*np.log(2) - special.gammaln(nvar/2) |
|
18 |
|
self.flex = self.current_r = np.sqrt(self.nvar-1) |
|
19 |
|
self.flex_pf = self.current_pf = self.get_pf(self.flex) |
|
20 |
|
|
|
|
48 |
|
self.a = nvar/2 |
|
49 |
|
|
21 |
50 |
def get_pf(self, r): |
def get_pf(self, r): |
22 |
|
""" |
|
23 |
|
returns pf, i.e. complementary part of multidimensional Gaussian distribution |
|
24 |
|
""" |
|
25 |
|
if self.nvar == 1: |
|
26 |
|
#return 1 - 2**(1-nvar/2) / special.gamma(nvar/2) * (np.sqrt(np.pi)*special.erf(r/np.sqrt(2)))/np.sqrt(2) |
|
27 |
|
return 1 - special.erf(r/1.4142135623730951) |
|
28 |
|
elif self.nvar == 2: |
|
29 |
|
return np.exp(-r**2/2) |
|
30 |
|
elif self.nvar == 3: |
|
31 |
|
#return 1 - 2**(1-nvar/2) / special.gamma(nvar/2) * (np.exp(-r**2/2)*(np.sqrt(np.pi)*np.exp(r**2/2)*special.erf(r/np.sqrt(2))-np.sqrt(2)*r))/np.sqrt(2) |
|
32 |
|
return 1 - 0.5641895835477564 * (np.exp(-r**2/2)*(np.sqrt(np.pi)*np.exp(r**2/2)*special.erf(r/np.sqrt(2))-np.sqrt(2)*r)) |
|
33 |
|
elif self.nvar == 4: |
|
34 |
|
return (r**2/2+1)*np.exp(-r**2/2) |
|
35 |
|
elif self.nvar == 6: |
|
36 |
|
return (r**4+4*r**2+8)*np.exp(-r**2/2)/8 |
|
37 |
|
|
|
38 |
|
# nvar=8: (48-(r^6+6*r^4+24*r^2+48)*e^(-r^2/2) / 2**(nvar/2))/48 |
|
39 |
|
|
|
40 |
|
# hračička ve hračce |
|
41 |
|
# nemám žádnou jistotu, že tohle počítá přesněji |
|
42 |
|
# ale ve výsokých dimenzích aspoň počítá |
|
43 |
|
elif self.nvar % 2 == 0: # sudé |
|
44 |
|
poly = [1] |
|
45 |
|
for i in range(self.nvar-2, 0, -2): |
|
46 |
|
poly.append(0) |
|
47 |
|
poly.append(i*poly[-2]) |
|
48 |
|
return np.polyval(np.array(poly) / poly[-1], r) * np.exp(-r**2/2) |
|
49 |
|
|
|
50 |
|
else: |
|
51 |
|
try: |
|
52 |
|
pf = self.C * integrate.quad(lambda x: np.exp(-(x**2)/2)*x**(self.nvar-1), r, np.inf)[0] |
|
53 |
|
except OverflowError: |
|
54 |
|
pf = 1 - self.C * integrate.quad(lambda x: np.exp(-(x**2)/2)*x**(self.nvar-1), 0, r)[0] |
|
55 |
|
|
|
56 |
|
return pf |
|
57 |
|
|
|
|
51 |
|
"returns pf, i.e. complementary part of multidimensional Gaussian distribution" |
|
52 |
|
return sc.gammaincc(self.a, r**2/2) |
|
53 |
|
|
|
54 |
|
def get_ps(self, r): |
|
55 |
|
"returns probability of falling inside d-ball with radius R" |
|
56 |
|
return sc.gammainc(self.a, r**2/2) |
|
57 |
|
|
58 |
58 |
def get_r(self, desired_pf): |
def get_r(self, desired_pf): |
59 |
|
""" |
|
60 |
|
sball_inversion |
|
61 |
|
returns r |
|
62 |
|
""" |
|
63 |
|
if self.nvar == 2: |
|
64 |
|
return np.sqrt(-2*np.log(desired_pf)) |
|
65 |
|
elif self.flex_pf == desired_pf: |
|
66 |
|
return self.flex |
|
67 |
|
else: |
|
68 |
|
# je to jistější |
|
69 |
|
self.current_r = self.flex |
|
70 |
|
self.current_pf = previous_pf = self.flex_pf |
|
71 |
|
|
|
72 |
|
self.__do_iter(desired_pf) |
|
73 |
|
self.current_pf = self.get_pf(self.current_r) |
|
74 |
|
# hrůza |
|
75 |
|
# pokračujeme, dokud to nezkonverguje, přenejmenším pokud to konvergue a neosciluje. |
|
76 |
|
while self.current_pf != previous_pf and self.current_pf != desired_pf\ |
|
77 |
|
and (self.current_pf > desired_pf or previous_pf < desired_pf): |
|
78 |
|
|
|
79 |
|
previous_pf = self.current_pf |
|
80 |
|
self.__do_iter(desired_pf) |
|
81 |
|
self.current_pf = self.get_pf(self.current_r) |
|
82 |
|
|
|
83 |
|
return self.current_r |
|
84 |
|
|
|
85 |
|
def __do_iter(self, desired_pf): |
|
86 |
|
r = self.current_r |
|
87 |
|
denominator = (self.C * np.exp(-(r**2)/2)*r**(self.nvar-1)) |
|
88 |
|
if denominator != 0 and not np.isnan(denominator): |
|
89 |
|
self.current_r += (self.current_pf - desired_pf) / denominator |
|
90 |
|
else: |
|
91 |
|
# zkombinujeme C a r^nvar, ale stejně nikoho to nezahraní |
|
92 |
|
log_delta = np.log(abs(self.current_pf - desired_pf)) + (r**2)/2 - (np.log(r)*(self.nvar-1) + self.logC) |
|
93 |
|
self.current_r += np.exp(log_delta)*np.sign(self.current_pf - desired_pf) |
|
94 |
|
|
|
95 |
|
if self.current_r < 0: |
|
96 |
|
self.current_r = r/2 |
|
97 |
|
|
|
98 |
|
|
|
|
59 |
|
"sball inversion. Returns radius of the s-ball with probability pf outside" |
|
60 |
|
rsqdiv2 = sc.gammainccinv(self.a, desired_pf) |
|
61 |
|
return np.sqrt(2*rsqdiv2) #radius |
|
62 |
|
|
|
63 |
|
|
99 |
64 |
def get_r_iteration(self, desired_pf): |
def get_r_iteration(self, desired_pf): |
100 |
|
""" |
|
101 |
|
Same as get_r, but do just one iteration |
|
102 |
|
""" |
|
103 |
|
|
|
104 |
|
|
|
105 |
|
if self.nvar == 2: |
|
106 |
|
return np.sqrt(-2*np.log(desired_pf)), desired_pf |
|
107 |
|
|
|
108 |
|
# logaritmus je na nulu citelný |
|
109 |
|
elif self.current_pf - desired_pf != 0: |
|
110 |
|
|
|
111 |
|
# hrůza, nečitelný |
|
112 |
|
# pokud je současné r-ko v jiné straně od chtěného r-ka, tak se vrátíme do inflexního bodu |
|
113 |
|
if (self.flex_pf > self.current_pf) is (self.flex_pf < desired_pf): |
|
114 |
|
# vstupní kontrola |
|
115 |
|
self.current_r = self.flex |
|
116 |
|
self.current_pf = self.flex_pf |
|
117 |
|
|
|
118 |
|
|
|
119 |
|
r = self.current_r # pro výstupní kontrolu |
|
120 |
|
|
|
121 |
|
self.__do_iter(desired_pf) |
|
122 |
|
|
|
123 |
|
|
|
124 |
|
# vystupní kontrola |
|
125 |
|
if (self.flex > self.current_r) is (self.flex < r): |
|
126 |
|
# preskočili jsme inflexní bod |
|
127 |
|
self.current_r = self.flex |
|
128 |
|
self.current_pf = self.flex_pf |
|
129 |
|
# ještě jednou |
|
130 |
|
self.__do_iter(desired_pf) |
|
131 |
|
|
|
132 |
|
self.current_pf = self.get_pf(self.current_r) # ne že bychom pf potrebovali v tomto kroce, ale... |
|
133 |
|
return self.current_r, self.current_pf |
|
134 |
|
|
|
135 |
|
|
|
|
65 |
|
"Same as .get_r(), just keeps compatibility with previous versions" |
|
66 |
|
return self.get_r(desired_pf), desired_pf |
|
67 |
|
|
|
68 |
|
|
|
69 |
|
|
|
70 |
|
class Sball_2D(Sball_nD): |
|
71 |
|
def get_pf(self, r): |
|
72 |
|
"returns pf, i.e. complementary part of multidimensional Gaussian distribution" |
|
73 |
|
return np.exp(-r**2/2) |
|
74 |
|
|
|
75 |
|
def get_r(self, desired_pf): |
|
76 |
|
"sball inversion. Returns radius of the s-ball with probability pf outside" |
|
77 |
|
return np.sqrt(-2*np.log(desired_pf)) |
|
78 |
|
|
|
79 |
|
|
|
80 |
|
# calculation is as fast as Sball_nD |
|
81 |
|
# but I'm not sure about precision |
|
82 |
|
class Sball_4D(Sball_nD): |
|
83 |
|
def get_pf(self, r): |
|
84 |
|
"returns pf, i.e. complementary part of multidimensional Gaussian distribution" |
|
85 |
|
return (r**2/2+1)*np.exp(-r**2/2) |
|
86 |
|
|
136 |
87 |
|
|
137 |
88 |
|
|