Skip to content

Commit 003f765

Browse files
committed
added doc-strings for model curves
1 parent f5790f8 commit 003f765

25 files changed

Lines changed: 636 additions & 257 deletions

src/models/arctangent_step.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
use super::common::{is_finite_non_negative, scale_and_mirror_upper_hessian, stabilize_hessian};
22
use ndarray::Array2;
33

4+
/// Вычисляет арктангенс-ступень:
5+
/// `f(x) = amplitude * atan(slope * (x - x0)) + offset`,
6+
/// где:
7+
/// - `amplitude` — амплитуда перехода,
8+
/// - `slope` — крутизна перехода,
9+
/// - `x0` — центр перехода по оси `x`,
10+
/// - `offset` — вертикальный сдвиг.
411
#[inline]
512
pub(super) fn eval(param: &[f64], x: f64) -> f64 {
6-
param[0] * (param[1] * (x - param[2])).atan() + param[3]
13+
let amplitude = param[0];
14+
let slope = param[1];
15+
let x0 = param[2];
16+
let offset = param[3];
17+
amplitude * (slope * (x - x0)).atan() + offset
718
}
819

920
pub(super) fn accumulate_gradient<L>(
@@ -16,20 +27,24 @@ pub(super) fn accumulate_gradient<L>(
1627
L: FnMut(f64, f64) -> f64,
1728
{
1829
debug_assert_eq!(x_values.len(), y_values.len());
30+
let amplitude = param[0];
31+
let slope = param[1];
32+
let x0 = param[2];
33+
let offset = param[3];
1934

2035
let mut index = 0;
2136
while index < x_values.len() {
2237
let x = x_values[index];
2338
let y = y_values[index];
24-
let z = param[1] * (x - param[2]);
39+
let z = slope * (x - x0);
2540
let atan_z = z.atan();
2641
let inv_den = 1.0 / (1.0 + z * z);
27-
let model = param[0] * atan_z + param[3];
42+
let model = amplitude * atan_z + offset;
2843
let residual = loss_derivative_from_prediction(model, y);
2944

3045
gradient[0] += residual * atan_z;
31-
gradient[1] += residual * (param[0] * (x - param[2]) * inv_den);
32-
gradient[2] += residual * (-param[0] * param[1] * inv_den);
46+
gradient[1] += residual * (amplitude * (x - x0) * inv_den);
47+
gradient[2] += residual * (-amplitude * slope * inv_den);
3348
gradient[3] += residual;
3449
index += 1;
3550
}
@@ -53,20 +68,21 @@ where
5368
let sample_count = x_values.len();
5469
let sample_scale = 1.0 / sample_count as f64;
5570
let mut hessian = Array2::zeros((4, 4));
71+
let amplitude = param[0];
72+
let slope = param[1];
73+
let x0 = param[2];
74+
let offset = param[3];
5675

5776
let mut index = 0;
5877
while index < sample_count {
5978
let x = x_values[index];
6079
let y = y_values[index];
61-
let a = param[0];
62-
let b = param[1];
63-
let c = param[2];
64-
let u = x - c;
65-
let z = b * u;
80+
let u = x - x0;
81+
let z = slope * u;
6682
let atan_z = z.atan();
6783
let inv_den = 1.0 / (1.0 + z * z);
6884
let d2_shape_dz2 = -2.0 * z * inv_den * inv_den;
69-
let model = a * atan_z + param[3];
85+
let model = amplitude * atan_z + offset;
7086
if !model.is_finite() {
7187
return None;
7288
}
@@ -78,15 +94,15 @@ where
7894
}
7995

8096
let jac_a = atan_z;
81-
let jac_b = a * inv_den * u;
82-
let jac_c = -a * inv_den * b;
97+
let jac_b = amplitude * inv_den * u;
98+
let jac_c = -amplitude * inv_den * slope;
8399
let jac_d = 1.0;
84100

85101
let d2_model_dadb = inv_den * u;
86-
let d2_model_dadc = -inv_den * b;
87-
let d2_model_dbdb = a * d2_shape_dz2 * u * u;
88-
let d2_model_dbdc = a * (d2_shape_dz2 * (-b) * u - inv_den);
89-
let d2_model_dcdc = a * d2_shape_dz2 * b * b;
102+
let d2_model_dadc = -inv_den * slope;
103+
let d2_model_dbdb = amplitude * d2_shape_dz2 * u * u;
104+
let d2_model_dbdc = amplitude * (d2_shape_dz2 * (-slope) * u - inv_den);
105+
let d2_model_dcdc = amplitude * d2_shape_dz2 * slope * slope;
90106

91107
hessian[[0, 0]] += loss_second * jac_a * jac_a;
92108
hessian[[0, 1]] += loss_second * jac_a * jac_b + loss_first * d2_model_dadb;

src/models/arrhenius.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@ use super::common::{
33
};
44
use ndarray::Array2;
55

6+
/// Вычисляет кривую Аррениуса:
7+
/// `f(x) = prefactor * exp(temp_coeff / x)`,
8+
/// где:
9+
/// - `prefactor` — масштабный коэффициент,
10+
/// - `temp_coeff` — параметр температурной чувствительности.
11+
///
12+
/// Значение `x` предварительно ограничивается снизу через `positive_x`.
613
#[inline]
714
pub(super) fn eval(param: &[f64], x: f64) -> f64 {
815
let x = positive_x(x);
9-
param[0] * (param[1] / x).exp()
16+
let prefactor = param[0];
17+
let temp_coeff = param[1];
18+
prefactor * (temp_coeff / x).exp()
1019
}
1120

1221
pub(super) fn accumulate_gradient<L>(
@@ -19,16 +28,18 @@ pub(super) fn accumulate_gradient<L>(
1928
L: FnMut(f64, f64) -> f64,
2029
{
2130
debug_assert_eq!(x_values.len(), y_values.len());
31+
let prefactor = param[0];
32+
let temp_coeff = param[1];
2233

2334
let mut index = 0;
2435
while index < x_values.len() {
2536
let x = positive_x(x_values[index]);
2637
let y = y_values[index];
27-
let exp_term = (param[1] / x).exp();
28-
let model = param[0] * exp_term;
38+
let exp_term = (temp_coeff / x).exp();
39+
let model = prefactor * exp_term;
2940
let residual = loss_derivative_from_prediction(model, y);
3041
gradient[0] += residual * exp_term;
31-
gradient[1] += residual * (param[0] * exp_term / x);
42+
gradient[1] += residual * (prefactor * exp_term / x);
3243
index += 1;
3344
}
3445
}
@@ -51,14 +62,16 @@ where
5162
let sample_count = x_values.len();
5263
let sample_scale = 1.0 / sample_count as f64;
5364
let mut hessian = Array2::zeros((2, 2));
65+
let prefactor = param[0];
66+
let temp_coeff = param[1];
5467

5568
let mut index = 0;
5669
while index < sample_count {
5770
let x = positive_x(x_values[index]);
5871
let y = y_values[index];
59-
let exp_term = (param[1] / x).exp();
72+
let exp_term = (temp_coeff / x).exp();
6073
let inv_x = 1.0 / x;
61-
let model = param[0] * exp_term;
74+
let model = prefactor * exp_term;
6275
if !model.is_finite() {
6376
return None;
6477
}
@@ -70,9 +83,9 @@ where
7083
}
7184

7285
let jac_a = exp_term;
73-
let jac_b = param[0] * exp_term * inv_x;
86+
let jac_b = prefactor * exp_term * inv_x;
7487
let d2_model_dadb = exp_term * inv_x;
75-
let d2_model_dbdb = param[0] * exp_term * inv_x * inv_x;
88+
let d2_model_dbdb = prefactor * exp_term * inv_x * inv_x;
7689

7790
hessian[[0, 0]] += loss_second * jac_a * jac_a;
7891
hessian[[0, 1]] += loss_second * jac_a * jac_b + loss_first * d2_model_dadb;

src/models/bi_exponential.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
use super::common::{is_finite_non_negative, scale_and_mirror_upper_hessian, stabilize_hessian};
22
use ndarray::Array2;
33

4+
/// Вычисляет сумму двух экспонент:
5+
/// `f(x) = a1 * exp(-k1 * x) + a2 * exp(-k2 * x) + offset`,
6+
/// где:
7+
/// - `a1`, `a2` — амплитуды экспоненциальных компонент,
8+
/// - `k1`, `k2` — коэффициенты затухания компонент,
9+
/// - `offset` — вертикальный сдвиг.
410
#[inline]
511
pub(super) fn eval(param: &[f64], x: f64) -> f64 {
6-
param[0] * (-param[1] * x).exp() + param[2] * (-param[3] * x).exp() + param[4]
12+
let a1 = param[0];
13+
let k1 = param[1];
14+
let a2 = param[2];
15+
let k2 = param[3];
16+
let offset = param[4];
17+
a1 * (-k1 * x).exp() + a2 * (-k2 * x).exp() + offset
718
}
819

920
pub(super) fn accumulate_gradient<L>(
@@ -16,20 +27,25 @@ pub(super) fn accumulate_gradient<L>(
1627
L: FnMut(f64, f64) -> f64,
1728
{
1829
debug_assert_eq!(x_values.len(), y_values.len());
30+
let a1 = param[0];
31+
let k1 = param[1];
32+
let a2 = param[2];
33+
let k2 = param[3];
34+
let offset = param[4];
1935

2036
let mut index = 0;
2137
while index < x_values.len() {
2238
let x = x_values[index];
2339
let y = y_values[index];
24-
let exp1 = (-param[1] * x).exp();
25-
let exp2 = (-param[3] * x).exp();
26-
let model = param[0] * exp1 + param[2] * exp2 + param[4];
40+
let exp1 = (-k1 * x).exp();
41+
let exp2 = (-k2 * x).exp();
42+
let model = a1 * exp1 + a2 * exp2 + offset;
2743
let residual = loss_derivative_from_prediction(model, y);
2844

2945
gradient[0] += residual * exp1;
30-
gradient[1] += residual * (-param[0] * x * exp1);
46+
gradient[1] += residual * (-a1 * x * exp1);
3147
gradient[2] += residual * exp2;
32-
gradient[3] += residual * (-param[2] * x * exp2);
48+
gradient[3] += residual * (-a2 * x * exp2);
3349
gradient[4] += residual;
3450
index += 1;
3551
}
@@ -53,14 +69,19 @@ where
5369
let sample_count = x_values.len();
5470
let sample_scale = 1.0 / sample_count as f64;
5571
let mut hessian = Array2::zeros((5, 5));
72+
let a1 = param[0];
73+
let k1 = param[1];
74+
let a2 = param[2];
75+
let k2 = param[3];
76+
let offset = param[4];
5677

5778
let mut index = 0;
5879
while index < sample_count {
5980
let x = x_values[index];
6081
let y = y_values[index];
61-
let exp1 = (-param[1] * x).exp();
62-
let exp2 = (-param[3] * x).exp();
63-
let model = param[0] * exp1 + param[2] * exp2 + param[4];
82+
let exp1 = (-k1 * x).exp();
83+
let exp2 = (-k2 * x).exp();
84+
let model = a1 * exp1 + a2 * exp2 + offset;
6485
if !model.is_finite() {
6586
return None;
6687
}
@@ -72,15 +93,15 @@ where
7293
}
7394

7495
let jac_a1 = exp1;
75-
let jac_k1 = -param[0] * x * exp1;
96+
let jac_k1 = -a1 * x * exp1;
7697
let jac_a2 = exp2;
77-
let jac_k2 = -param[2] * x * exp2;
98+
let jac_k2 = -a2 * x * exp2;
7899
let jac_c = 1.0;
79100

80101
let d2_model_da1dk1 = -x * exp1;
81-
let d2_model_dk1dk1 = param[0] * x * x * exp1;
102+
let d2_model_dk1dk1 = a1 * x * x * exp1;
82103
let d2_model_da2dk2 = -x * exp2;
83-
let d2_model_dk2dk2 = param[2] * x * x * exp2;
104+
let d2_model_dk2dk2 = a2 * x * x * exp2;
84105

85106
hessian[[0, 0]] += loss_second * jac_a1 * jac_a1;
86107
hessian[[0, 1]] += loss_second * jac_a1 * jac_k1 + loss_first * d2_model_da1dk1;

src/models/damped_sinusoid.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
use ndarray::Array2;
2+
3+
/// Вычисляет затухающую синусоиду:
4+
/// `f(x) = amplitude * exp(-damping * x) * sin(omega * x + phi) + offset`,
5+
/// где:
6+
/// - `amplitude` — начальная амплитуда,
7+
/// - `damping` — коэффициент затухания,
8+
/// - `omega` — угловая частота,
9+
/// - `phi` — фазовый сдвиг,
10+
/// - `offset` — вертикальный сдвиг.
211
#[inline]
312
pub(super) fn eval(param: &[f64], x: f64) -> f64 {
4-
param[0] * (-param[1] * x).exp() * (param[2] * x + param[3]).sin() + param[4]
13+
let amplitude = param[0];
14+
let damping = param[1];
15+
let omega = param[2];
16+
let phi = param[3];
17+
let offset = param[4];
18+
amplitude * (-damping * x).exp() * (omega * x + phi).sin() + offset
519
}
620

721
pub(super) fn accumulate_gradient<L>(
@@ -14,22 +28,27 @@ pub(super) fn accumulate_gradient<L>(
1428
L: FnMut(f64, f64) -> f64,
1529
{
1630
debug_assert_eq!(x_values.len(), y_values.len());
31+
let amplitude = param[0];
32+
let damping = param[1];
33+
let omega = param[2];
34+
let phi = param[3];
35+
let offset = param[4];
1736

1837
let mut index = 0;
1938
while index < x_values.len() {
2039
let x = x_values[index];
2140
let y = y_values[index];
22-
let exp_part = (-param[1] * x).exp();
23-
let angle = param[2] * x + param[3];
41+
let exp_part = (-damping * x).exp();
42+
let angle = omega * x + phi;
2443
let sin_part = angle.sin();
2544
let cos_part = angle.cos();
26-
let model = param[0] * exp_part * sin_part + param[4];
45+
let model = amplitude * exp_part * sin_part + offset;
2746
let residual = loss_derivative_from_prediction(model, y);
2847

2948
gradient[0] += residual * exp_part * sin_part;
30-
gradient[1] += residual * (-param[0] * x * exp_part * sin_part);
31-
gradient[2] += residual * (param[0] * exp_part * cos_part * x);
32-
gradient[3] += residual * (param[0] * exp_part * cos_part);
49+
gradient[1] += residual * (-amplitude * x * exp_part * sin_part);
50+
gradient[2] += residual * (amplitude * exp_part * cos_part * x);
51+
gradient[3] += residual * (amplitude * exp_part * cos_part);
3352
gradient[4] += residual;
3453
index += 1;
3554
}

src/models/emg.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,38 @@ use super::common::{erfc_approx, positive_param_with_derivative};
22
use ndarray::Array2;
33

44
#[inline]
5-
fn eval_right(a: f64, mu: f64, sigma: f64, tau: f64, c: f64, x: f64) -> f64 {
5+
fn eval_right(amplitude: f64, mu: f64, sigma: f64, tau: f64, baseline: f64, x: f64) -> f64 {
66
let (sigma, _) = positive_param_with_derivative(sigma);
77
let (tau, _) = positive_param_with_derivative(tau);
88
let delta = x - mu;
99
let z = (sigma / tau - delta / sigma) / std::f64::consts::SQRT_2;
1010
let exponent = sigma * sigma / (2.0 * tau * tau) - delta / tau;
11-
c + (a / (2.0 * tau)) * exponent.exp() * erfc_approx(z)
11+
baseline + (amplitude / (2.0 * tau)) * exponent.exp() * erfc_approx(z)
1212
}
1313

1414
#[inline]
15+
/// Вычисляет экспоненциально-модифицированную гауссиану (EMG):
16+
/// `f(x) = baseline + (amplitude / (2 * tau)) * exp(sigma^2 / (2 * tau^2) - (x - mu) / tau) * erfc(z)`,
17+
/// где:
18+
/// - `amplitude` — амплитуда,
19+
/// - `mu` — центр гауссовой части,
20+
/// - `sigma` — ширина гауссовой части,
21+
/// - `tau` — экспоненциальная постоянная,
22+
/// - `baseline` — вертикальный сдвиг.
23+
///
24+
/// Для `tau < 0` используется отражение по `mu`, что даёт хвост в противоположную сторону.
1525
pub(super) fn eval(param: &[f64], x: f64) -> f64 {
16-
if param[3].is_sign_negative() {
17-
let reflected_x = 2.0 * param[1] - x;
18-
eval_right(
19-
param[0],
20-
param[1],
21-
param[2],
22-
param[3].abs(),
23-
param[4],
24-
reflected_x,
25-
)
26+
let amplitude = param[0];
27+
let mu = param[1];
28+
let sigma = param[2];
29+
let tau = param[3];
30+
let baseline = param[4];
31+
32+
if tau.is_sign_negative() {
33+
let reflected_x = 2.0 * mu - x;
34+
eval_right(amplitude, mu, sigma, tau.abs(), baseline, reflected_x)
2635
} else {
27-
eval_right(param[0], param[1], param[2], param[3], param[4], x)
36+
eval_right(amplitude, mu, sigma, tau, baseline, x)
2837
}
2938
}
3039

0 commit comments

Comments
 (0)