bezierv 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bezierv/__init__.py +0 -0
- bezierv/algorithms/__init__.py +0 -0
- bezierv/algorithms/conv_bezierv.py +113 -0
- bezierv/algorithms/nelder_mead.py +157 -0
- bezierv/algorithms/non_linear.py +167 -0
- bezierv/algorithms/non_linear_solver.py +186 -0
- bezierv/algorithms/proj_grad.py +132 -0
- bezierv/algorithms/proj_subgrad.py +203 -0
- bezierv/algorithms/utils.py +67 -0
- bezierv/classes/__init__.py +0 -0
- bezierv/classes/bezierv.py +579 -0
- bezierv/classes/convolver.py +74 -0
- bezierv/classes/distfit.py +216 -0
- bezierv/tests/__init__.py +0 -0
- bezierv/tests/conf_test.py +0 -0
- bezierv/tests/test_algorithms/test_conv_bezierv.py +0 -0
- bezierv/tests/test_algorithms/test_nelder_mead.py +54 -0
- bezierv/tests/test_algorithms/test_proj_grad.py +63 -0
- bezierv/tests/test_algorithms/test_proj_subgrad.py +65 -0
- bezierv/tests/test_algorithms/test_utils.py +42 -0
- bezierv/tests/test_classes/conftest.py +42 -0
- bezierv/tests/test_classes/test_bezierv.py +0 -0
- bezierv/tests/test_classes/test_convolver.py +36 -0
- bezierv/tests/test_classes/test_distfit.py +34 -0
- bezierv-0.1.0.dist-info/METADATA +32 -0
- bezierv-0.1.0.dist-info/RECORD +29 -0
- bezierv-0.1.0.dist-info/WHEEL +5 -0
- bezierv-0.1.0.dist-info/licenses/LICENSE +21 -0
- bezierv-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from bezierv.classes.bezierv import Bezierv
|
3
|
+
|
4
|
+
def grad(n: int,
|
5
|
+
m:int,
|
6
|
+
t: float,
|
7
|
+
bezierv: Bezierv,
|
8
|
+
controls_z: np.array,
|
9
|
+
emp_cdf_data: np.array) -> np.array:
|
10
|
+
"""
|
11
|
+
Compute the gradient of the fitting objective with respect to the z control points.
|
12
|
+
|
13
|
+
Parameters
|
14
|
+
----------
|
15
|
+
n : int
|
16
|
+
The number of control points minus one.
|
17
|
+
m : int
|
18
|
+
The number of empirical CDF data points.
|
19
|
+
t : np.array
|
20
|
+
The parameter values corresponding to the data points. Expected to be an array of shape (m,).
|
21
|
+
bezierv : Bezierv
|
22
|
+
An instance of the Bezierv class representing the Bezier random variable.
|
23
|
+
controls_z : np.array
|
24
|
+
The current z-coordinates of the control points.
|
25
|
+
emp_cdf_data : np.array
|
26
|
+
The empirical CDF data points used for fitting.
|
27
|
+
|
28
|
+
Returns
|
29
|
+
-------
|
30
|
+
np.array
|
31
|
+
An array representing the gradient with respect to each z control point.
|
32
|
+
"""
|
33
|
+
grad_z = np.zeros(n + 1)
|
34
|
+
for j in range(m):
|
35
|
+
inner_sum = np.zeros(n + 1)
|
36
|
+
for i in range(n + 1):
|
37
|
+
inner_sum[i] = bezierv.bernstein(t[j], i, bezierv.comb, n)
|
38
|
+
grad_z += 2 * (bezierv.poly_z(t[j], controls_z) - emp_cdf_data[j]) * inner_sum
|
39
|
+
return grad_z
|
40
|
+
|
41
|
+
def project_z(controls_z: np.array):
|
42
|
+
"""
|
43
|
+
Project the z control points onto the feasible set.
|
44
|
+
|
45
|
+
The projection is performed by first clipping the control points to the [0, 1] interval,
|
46
|
+
sorting them in ascending order, and then enforcing the boundary conditions by setting
|
47
|
+
the first control point to 0 and the last control point to 1.
|
48
|
+
|
49
|
+
Parameters
|
50
|
+
----------
|
51
|
+
controls_z : np.array
|
52
|
+
The current z-coordinates of the control points.
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
np.array
|
57
|
+
The projected z control points that satisfy the constraints.
|
58
|
+
"""
|
59
|
+
z_prime = np.clip(controls_z, a_min= 0, a_max=1)
|
60
|
+
z_prime.sort()
|
61
|
+
z_prime[0] = 0
|
62
|
+
z_prime[-1] = 1
|
63
|
+
return z_prime
|
64
|
+
|
65
|
+
def fit(n: int,
|
66
|
+
m: int,
|
67
|
+
data: np.array,
|
68
|
+
bezierv: Bezierv,
|
69
|
+
init_x: np.array,
|
70
|
+
init_z: np.array,
|
71
|
+
t: float,
|
72
|
+
emp_cdf_data: np.array,
|
73
|
+
step_size: float,
|
74
|
+
max_iter: int,
|
75
|
+
threshold: float) -> tuple:
|
76
|
+
"""
|
77
|
+
Fit the Bezier random variable to the empirical CDF data using projected gradient descent.
|
78
|
+
|
79
|
+
Starting from an initial guess for the z control points, this method iteratively
|
80
|
+
updates the z control points by taking gradient descent steps and projecting the
|
81
|
+
result back onto the feasible set. The process continues until convergence is
|
82
|
+
achieved (i.e., the change in control points is less than a set threshold) or until
|
83
|
+
the maximum number of iterations is reached. After convergence, the Bezierv curve is
|
84
|
+
updated with the new control points and the fitting error is computed.
|
85
|
+
|
86
|
+
Parameters
|
87
|
+
----------
|
88
|
+
n : int
|
89
|
+
The number of control points minus one.
|
90
|
+
m : int
|
91
|
+
The number of empirical CDF data points.
|
92
|
+
data : np.array
|
93
|
+
The sorted data points used to fit the Bezier distribution.
|
94
|
+
bezierv : Bezierv
|
95
|
+
An instance of the Bezierv class representing the Bezier random variable.
|
96
|
+
init_x : np.array
|
97
|
+
Initial guess for the x-coordinates of the control points.
|
98
|
+
init_z : np.array
|
99
|
+
Initial guess for the z-coordinates of the control points.
|
100
|
+
t : float or np.array
|
101
|
+
The parameter values corresponding to the data points. Expected to be an array of shape (m,).
|
102
|
+
emp_cdf_data : np.array
|
103
|
+
The empirical CDF data points used for fitting.
|
104
|
+
step_size : float
|
105
|
+
The step size for the gradient descent updates.
|
106
|
+
max_iter : int
|
107
|
+
The maximum number of iterations allowed for the fitting process.
|
108
|
+
threshold : float
|
109
|
+
The convergence threshold for the change in control points.
|
110
|
+
|
111
|
+
Returns
|
112
|
+
-------
|
113
|
+
Bezierv
|
114
|
+
The updated Bezierv instance with fitted control points.
|
115
|
+
float
|
116
|
+
The mean squared error (MSE) of the fit.
|
117
|
+
"""
|
118
|
+
z = init_z
|
119
|
+
for i in range(max_iter):
|
120
|
+
grad_z = grad(n, m, t, bezierv, z, emp_cdf_data)
|
121
|
+
z_prime = project_z(z - step_size * grad_z)
|
122
|
+
if np.linalg.norm(z_prime - z) < threshold:
|
123
|
+
z = z_prime
|
124
|
+
break
|
125
|
+
z = z_prime
|
126
|
+
|
127
|
+
se = 0
|
128
|
+
for j in range(m):
|
129
|
+
se += (bezierv.poly_z(t[j], z) - emp_cdf_data[j])**2
|
130
|
+
bezierv.update_bezierv(init_x, z)
|
131
|
+
mse = se/m
|
132
|
+
return bezierv, mse
|
@@ -0,0 +1,203 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from bezierv.classes.bezierv import Bezierv
|
3
|
+
from bezierv.algorithms import utils as utils
|
4
|
+
|
5
|
+
def subgrad(n: int,
|
6
|
+
m: int,
|
7
|
+
bezierv: Bezierv,
|
8
|
+
t: float,
|
9
|
+
controls_z: np.array,
|
10
|
+
emp_cdf_data: np.array) -> tuple:
|
11
|
+
"""
|
12
|
+
Compute the subgradient of the fitting objective with respect to the (x, z) control points.
|
13
|
+
|
14
|
+
Parameters
|
15
|
+
----------
|
16
|
+
n : int
|
17
|
+
The number of control points minus one.
|
18
|
+
m : int
|
19
|
+
The number of empirical CDF data points.
|
20
|
+
bezierv : Bezierv
|
21
|
+
An instance of the Bezierv class representing the Bezier random variable.
|
22
|
+
t : np.array
|
23
|
+
The parameter values corresponding to the data points. Expected to be an array of shape (m,).
|
24
|
+
controls_z : np.array
|
25
|
+
The current z-coordinates of the control points.
|
26
|
+
emp_cdf_data : np.array
|
27
|
+
The empirical CDF data points used for fitting.
|
28
|
+
|
29
|
+
Returns
|
30
|
+
-------
|
31
|
+
np.array, np.array
|
32
|
+
A tuple containing the subgradient with respect to the x control points and the gradient with respect to the z control points.
|
33
|
+
"""
|
34
|
+
grad_z = np.zeros(n + 1)
|
35
|
+
subgrad_x = np.zeros(n + 1)
|
36
|
+
for j in range(m):
|
37
|
+
inner_sum = np.zeros(n + 1)
|
38
|
+
for i in range(n + 1):
|
39
|
+
inner_sum[i] = bezierv.bernstein(t[j], i, bezierv.comb, n)
|
40
|
+
|
41
|
+
subgrad_x += 2 * (bezierv.poly_z(t[j], controls_z) - emp_cdf_data[j]) * (1 / m) * inner_sum
|
42
|
+
grad_z += 2 * (bezierv.poly_z(t[j], controls_z) - emp_cdf_data[j]) * inner_sum
|
43
|
+
|
44
|
+
grad_z = np.zeros(n + 1)
|
45
|
+
for j in range(m):
|
46
|
+
inner_sum = np.zeros(n + 1)
|
47
|
+
for i in range(n + 1):
|
48
|
+
inner_sum[i] = bezierv.bernstein(t[j], i, bezierv.comb, n)
|
49
|
+
|
50
|
+
grad_z += 2 * (bezierv.poly_z(t[j], controls_z) - emp_cdf_data[j]) * inner_sum
|
51
|
+
|
52
|
+
return subgrad_x, grad_z
|
53
|
+
|
54
|
+
def project_x(data:np.array,
|
55
|
+
controls_x: np.array):
|
56
|
+
"""
|
57
|
+
Project the x control points onto the feasible set.
|
58
|
+
|
59
|
+
The projection is performed by first clipping the control points to the [X_(1), X_(m)] interval,
|
60
|
+
sorting them in ascending order, and then enforcing the boundary conditions by setting
|
61
|
+
the first control point to X_(1) and the last control point to X_(m).
|
62
|
+
|
63
|
+
Parameters
|
64
|
+
----------
|
65
|
+
controls_x : np.array
|
66
|
+
The current z-coordinates of the control points.
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
np.array
|
71
|
+
The projected z control points that satisfy the constraints.
|
72
|
+
"""
|
73
|
+
x_prime = np.clip(controls_x, a_min= data[0], a_max=data[-1])
|
74
|
+
x_prime.sort()
|
75
|
+
x_prime[0] = data[0]
|
76
|
+
x_prime[-1] = data[-1]
|
77
|
+
return x_prime
|
78
|
+
|
79
|
+
def project_z(controls_z: np.array):
|
80
|
+
"""
|
81
|
+
Project the z control points onto the feasible set.
|
82
|
+
|
83
|
+
The projection is performed by first clipping the control points to the [0, 1] interval,
|
84
|
+
sorting them in ascending order, and then enforcing the boundary conditions by setting
|
85
|
+
the first control point to 0 and the last control point to 1.
|
86
|
+
|
87
|
+
Parameters
|
88
|
+
----------
|
89
|
+
controls_z : np.array
|
90
|
+
The current z-coordinates of the control points.
|
91
|
+
|
92
|
+
Returns
|
93
|
+
-------
|
94
|
+
np.array
|
95
|
+
The projected z control points that satisfy the constraints.
|
96
|
+
"""
|
97
|
+
z_prime = np.clip(controls_z, a_min= 0, a_max=1)
|
98
|
+
z_prime.sort()
|
99
|
+
z_prime[0] = 0
|
100
|
+
z_prime[-1] = 1
|
101
|
+
return z_prime
|
102
|
+
|
103
|
+
def objective_function(m: int,
|
104
|
+
bezierv: Bezierv,
|
105
|
+
emp_cdf_data: np.array,
|
106
|
+
z: np.array,
|
107
|
+
t: np.array):
|
108
|
+
"""
|
109
|
+
Compute the objective function value for the given z control points.
|
110
|
+
|
111
|
+
This method calculates the sum of squared errors between the Bezier random variable's CDF
|
112
|
+
and the empirical CDF data.
|
113
|
+
|
114
|
+
Parameters
|
115
|
+
----------
|
116
|
+
z : np.array
|
117
|
+
The z-coordinates of the control points.
|
118
|
+
t : np.array
|
119
|
+
The parameter values corresponding to the data points.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
float
|
124
|
+
The value of the objective function (MSE).
|
125
|
+
"""
|
126
|
+
se = 0
|
127
|
+
for j in range(m):
|
128
|
+
se += (bezierv.poly_z(t[j], z) - emp_cdf_data[j])**2
|
129
|
+
return se / m
|
130
|
+
|
131
|
+
def fit(n: int,
|
132
|
+
m: int,
|
133
|
+
data: np.array,
|
134
|
+
bezierv: Bezierv,
|
135
|
+
init_x: np.array,
|
136
|
+
init_z: np.array,
|
137
|
+
init_t: float,
|
138
|
+
emp_cdf_data: np.array,
|
139
|
+
step_size: float,
|
140
|
+
max_iter: int):
|
141
|
+
"""
|
142
|
+
Fit the Bezier random variable to the empirical CDF data using projected gradient descent.
|
143
|
+
|
144
|
+
Starting from an initial guess for the z control points, this method iteratively
|
145
|
+
updates the z control points by taking gradient descent steps and projecting the
|
146
|
+
result back onto the feasible set. The process continues until convergence is
|
147
|
+
achieved (i.e., the change in control points is less than a set threshold) or until
|
148
|
+
the maximum number of iterations is reached. After convergence, the Bezierv curve is
|
149
|
+
updated with the new control points and the fitting error is computed.
|
150
|
+
|
151
|
+
Parameters
|
152
|
+
----------
|
153
|
+
n : int
|
154
|
+
The number of control points minus one.
|
155
|
+
m : int
|
156
|
+
The number of empirical CDF data points.
|
157
|
+
data : np.array
|
158
|
+
The empirical data to fit the Bezier random variable to.
|
159
|
+
bezierv : Bezierv
|
160
|
+
An instance of the Bezierv class representing the Bezier random variable.
|
161
|
+
init_x : np.array
|
162
|
+
Initial control points for the x-coordinates of the Bezier curve.
|
163
|
+
init_z : np.array
|
164
|
+
Initial control points for the z-coordinates of the Bezier curve.
|
165
|
+
init_t : float
|
166
|
+
Initial parameter values corresponding to the data points.
|
167
|
+
emp_cdf_data : np.array
|
168
|
+
The empirical cumulative distribution function (CDF) data derived from the empirical data.
|
169
|
+
step_size : float
|
170
|
+
The step size for the subgradient method updates.
|
171
|
+
max_iter : int
|
172
|
+
The maximum number of iterations to perform.
|
173
|
+
|
174
|
+
Returns
|
175
|
+
-------
|
176
|
+
Bezierv
|
177
|
+
The updated Bezierv instance with fitted control points.
|
178
|
+
float
|
179
|
+
The mean squared error (MSE) of the fit.
|
180
|
+
"""
|
181
|
+
f_best = np.inf
|
182
|
+
x_best = None
|
183
|
+
z_best = None
|
184
|
+
x = init_x
|
185
|
+
z = init_z
|
186
|
+
t = init_t
|
187
|
+
for i in range(max_iter):
|
188
|
+
subgrad_x, grad_z = subgrad(n, m, bezierv, t, z, emp_cdf_data)
|
189
|
+
z_prime = project_z(z - step_size * grad_z)
|
190
|
+
x_prime = project_x(data, x - step_size * subgrad_x)
|
191
|
+
t_prime = utils.get_t(n, m, data, bezierv, x_prime)
|
192
|
+
mse_prime = objective_function(m, bezierv, emp_cdf_data, z_prime, t_prime)
|
193
|
+
if mse_prime < f_best:
|
194
|
+
f_best = mse_prime
|
195
|
+
x_best = x_prime
|
196
|
+
z_best = z_prime
|
197
|
+
x = x_prime
|
198
|
+
z = z_prime
|
199
|
+
t = t_prime
|
200
|
+
|
201
|
+
bezierv.update_bezierv(x_best, z_best)
|
202
|
+
|
203
|
+
return bezierv, f_best
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from scipy.optimize import brentq
|
3
|
+
from bezierv.classes.bezierv import Bezierv
|
4
|
+
|
5
|
+
def root_find(n: int,
|
6
|
+
bezierv: Bezierv,
|
7
|
+
controls_x: np.array,
|
8
|
+
data_point: float) -> float:
|
9
|
+
"""
|
10
|
+
Find the parameter value t such that the Bezier random variable's x-coordinate equals data_i.
|
11
|
+
|
12
|
+
This method defines a function representing the difference between the x-coordinate
|
13
|
+
of the Bezier curve and the given data point. It then uses Brent's method to find a
|
14
|
+
root of this function, i.e., a value t in [0, 1] that satisfies the equality.
|
15
|
+
|
16
|
+
Parameters
|
17
|
+
----------
|
18
|
+
n : int
|
19
|
+
The number of control points minus one for the Bezier curve.
|
20
|
+
bezierv : Bezierv
|
21
|
+
An instance of the Bezierv class representing the Bezier random variable.
|
22
|
+
controls_x : np.array
|
23
|
+
The control points for the x-coordinates of the Bezier curve.
|
24
|
+
data_point : float
|
25
|
+
The data point for which to find the corresponding value of t.
|
26
|
+
|
27
|
+
Returns
|
28
|
+
-------
|
29
|
+
float
|
30
|
+
The value of t in the interval [0, 1] such that the Bezier random variable's x-coordinate
|
31
|
+
is approximately equal to data_point.
|
32
|
+
"""
|
33
|
+
def poly_x_sample(t, controls_x, data_point):
|
34
|
+
p_x = 0
|
35
|
+
for i in range(n + 1):
|
36
|
+
p_x += bezierv.bernstein(t, i, bezierv.comb, bezierv.n) * controls_x[i]
|
37
|
+
return p_x - data_point
|
38
|
+
t = brentq(poly_x_sample, 0, 1, args=(controls_x, data_point))
|
39
|
+
return t
|
40
|
+
|
41
|
+
def get_t(n: int,
|
42
|
+
m: int,
|
43
|
+
data: np.array,
|
44
|
+
bezierv: Bezierv,
|
45
|
+
controls_x: np.array) -> np.array:
|
46
|
+
"""
|
47
|
+
Compute values of the parameter t for each data point using root-finding.
|
48
|
+
|
49
|
+
For each sorted data point, this method finds the corresponding value 't' such that the
|
50
|
+
x-coordinate of the Bezier random variable matches the data point.
|
51
|
+
|
52
|
+
Parameters
|
53
|
+
----------
|
54
|
+
controls_x : np.array
|
55
|
+
The control points for the x-coordinates of the Bezier curve.
|
56
|
+
data : np.array
|
57
|
+
Array of data points for which to compute the corresponding values of t.
|
58
|
+
|
59
|
+
Returns
|
60
|
+
-------
|
61
|
+
np.array
|
62
|
+
An array of values of t corresponding to each data point.
|
63
|
+
"""
|
64
|
+
t = np.zeros(m)
|
65
|
+
for i in range(m):
|
66
|
+
t[i] = root_find(n, bezierv, controls_x, data[i])
|
67
|
+
return t
|
File without changes
|