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.
@@ -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