Skip to content

Commit c4ef582

Browse files
committed
added Gompertz
1 parent 9a989a7 commit c4ef582

10 files changed

Lines changed: 157 additions & 96 deletions

File tree

Cargo.lock

Lines changed: 20 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "curve-fit"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
edition = "2024"
55
authors = ["hexqnt <hexqntlab@gmail.com>"]
66
license = "Apache-2.0 OR MIT"

src/app.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ enum ModelChoice {
210210
Arrhenius,
211211
Inverse,
212212
Logistic,
213+
Gompertz,
213214
Lorentzian,
214215
NaturalLog,
215216
FourPl,
@@ -231,11 +232,12 @@ enum ModelChoice {
231232
}
232233

233234
impl ModelChoice {
234-
const ALL: [Self; 22] = [
235+
const ALL: [Self; 23] = [
235236
Self::Polynomial,
236237
Self::Arrhenius,
237238
Self::Inverse,
238239
Self::Logistic,
240+
Self::Gompertz,
239241
Self::Lorentzian,
240242
Self::NaturalLog,
241243
Self::FourPl,
@@ -277,6 +279,7 @@ impl ResolvedModel {
277279
ModelChoice::Arrhenius => Self::Parametric(CurveFamily::Arrhenius),
278280
ModelChoice::Inverse => Self::Parametric(CurveFamily::Inverse),
279281
ModelChoice::Logistic => Self::Parametric(CurveFamily::Logistic),
282+
ModelChoice::Gompertz => Self::Parametric(CurveFamily::Gompertz),
280283
ModelChoice::Lorentzian => Self::Parametric(CurveFamily::Lorentzian),
281284
ModelChoice::NaturalLog => Self::Parametric(CurveFamily::NaturalLog),
282285
ModelChoice::FourPl => Self::Parametric(CurveFamily::FourPl),
@@ -351,6 +354,7 @@ fn model_group(model: ModelChoice) -> ModelGroup {
351354
match model {
352355
ModelChoice::Polynomial => ModelGroup::Polynomial,
353356
ModelChoice::Logistic
357+
| ModelChoice::Gompertz
354358
| ModelChoice::FourPl
355359
| ModelChoice::FivePl
356360
| ModelChoice::HyperbolicTangent

src/app/formula.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fn model_formula_full(model: ModelChoice, polynomial_degree: usize) -> String {
1010
ModelChoice::Arrhenius => r"y = A·exp(\frac{B}{x})".to_string(),
1111
ModelChoice::Inverse => r"y = A + \frac{B}{x}".to_string(),
1212
ModelChoice::Logistic => r"y = \frac{A}{1 + exp(-B·(x - C))}".to_string(),
13+
ModelChoice::Gompertz => r"y = A·exp(-exp(-B·(x - C)))".to_string(),
1314
ModelChoice::Lorentzian => r"y = C + \frac{A}{1 + (\frac{x - x_0}{gamma})^{2}}".to_string(),
1415
ModelChoice::NaturalLog => r"y = A·ln(\frac{x}{B})".to_string(),
1516
ModelChoice::FourPl => r"y = d + \frac{a - d}{1 + (\frac{x}{c})^{b}}".to_string(),
@@ -106,6 +107,9 @@ fn model_ml_note(language: UiLanguage, model: ModelChoice) -> &'static str {
106107
(UiLanguage::English, ModelChoice::Logistic) => {
107108
"Sigmoid response model for bounded transitions."
108109
}
110+
(UiLanguage::English, ModelChoice::Gompertz) => {
111+
"Asymmetric sigmoid growth model with a long lower tail."
112+
}
109113
(UiLanguage::English, ModelChoice::Lorentzian) => "Peak-shaped model with heavy tails.",
110114
(UiLanguage::English, ModelChoice::NaturalLog) => {
111115
"Log transform response, useful for diminishing returns."
@@ -147,6 +151,9 @@ fn model_ml_note(language: UiLanguage, model: ModelChoice) -> &'static str {
147151
(UiLanguage::Russian, ModelChoice::Logistic) => {
148152
"Сигмоидальная модель ограниченного перехода."
149153
}
154+
(UiLanguage::Russian, ModelChoice::Gompertz) => {
155+
"Асимметричная сигмоида с длинным нижним хвостом."
156+
}
150157
(UiLanguage::Russian, ModelChoice::Lorentzian) => {
151158
"Пиковая модель с более тяжёлыми хвостами."
152159
}

src/app/i18n.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,17 +190,17 @@ pub(super) fn param_init_method_disabled_label(
190190
) -> &'static str {
191191
match (language, method) {
192192
(UiLanguage::English, ParamInitMethod::DataBased) => {
193-
"Data-based (Polynomial/Logistic/Gaussian/Exponential (Basic)/Power only)"
193+
"Data-based (Polynomial/Logistic/Gompertz/Gaussian/Exponential (Basic)/Power only)"
194194
}
195195
(UiLanguage::English, ParamInitMethod::Randomized) => {
196-
"Randomized (Polynomial/Logistic/Gaussian/Exponential (Basic)/Power only)"
196+
"Randomized (Polynomial/Logistic/Gompertz/Gaussian/Exponential (Basic)/Power only)"
197197
}
198198
(UiLanguage::English, ParamInitMethod::Default) => "Default",
199199
(UiLanguage::Russian, ParamInitMethod::DataBased) => {
200-
"По данным (только Polynomial/Logistic/Gaussian/Exponential (Basic)/Power)"
200+
"По данным (только Polynomial/Logistic/Gompertz/Gaussian/Exponential (Basic)/Power)"
201201
}
202202
(UiLanguage::Russian, ParamInitMethod::Randomized) => {
203-
"Случайно (только Polynomial/Logistic/Gaussian/Exponential (Basic)/Power)"
203+
"Случайно (только Polynomial/Logistic/Gompertz/Gaussian/Exponential (Basic)/Power)"
204204
}
205205
(UiLanguage::Russian, ParamInitMethod::Default) => "По умолчанию",
206206
}
@@ -212,6 +212,7 @@ pub(super) fn model_choice_label(language: UiLanguage, model: ModelChoice) -> &'
212212
(UiLanguage::English, ModelChoice::Arrhenius) => "Arrhenius",
213213
(UiLanguage::English, ModelChoice::Inverse) => "Inverse",
214214
(UiLanguage::English, ModelChoice::Logistic) => "Logistic",
215+
(UiLanguage::English, ModelChoice::Gompertz) => "Gompertz",
215216
(UiLanguage::English, ModelChoice::Lorentzian) => "Lorentzian",
216217
(UiLanguage::English, ModelChoice::NaturalLog) => "Natural Log",
217218
(UiLanguage::English, ModelChoice::FourPl) => "4PL",
@@ -234,6 +235,7 @@ pub(super) fn model_choice_label(language: UiLanguage, model: ModelChoice) -> &'
234235
(UiLanguage::Russian, ModelChoice::Arrhenius) => "Аррениус",
235236
(UiLanguage::Russian, ModelChoice::Inverse) => "Обратная",
236237
(UiLanguage::Russian, ModelChoice::Logistic) => "Логистическая",
238+
(UiLanguage::Russian, ModelChoice::Gompertz) => "Гомпертц",
237239
(UiLanguage::Russian, ModelChoice::Lorentzian) => "Лоренциан",
238240
(UiLanguage::Russian, ModelChoice::NaturalLog) => "Натуральный логарифм",
239241
(UiLanguage::Russian, ModelChoice::FourPl) => "4PL",
@@ -269,6 +271,7 @@ pub(super) fn family_label(language: UiLanguage, family: CurveFamily) -> &'stati
269271
(UiLanguage::English, CurveFamily::Arrhenius) => "Arrhenius",
270272
(UiLanguage::English, CurveFamily::Inverse) => "Inverse",
271273
(UiLanguage::English, CurveFamily::Logistic) => "Logistic",
274+
(UiLanguage::English, CurveFamily::Gompertz) => "Gompertz",
272275
(UiLanguage::English, CurveFamily::Lorentzian) => "Lorentzian",
273276
(UiLanguage::English, CurveFamily::NaturalLog) => "Natural Log",
274277
(UiLanguage::English, CurveFamily::FourPl) => "4PL",
@@ -295,6 +298,7 @@ pub(super) fn family_label(language: UiLanguage, family: CurveFamily) -> &'stati
295298
(UiLanguage::Russian, CurveFamily::Arrhenius) => "Аррениус",
296299
(UiLanguage::Russian, CurveFamily::Inverse) => "Обратная",
297300
(UiLanguage::Russian, CurveFamily::Logistic) => "Логистическая",
301+
(UiLanguage::Russian, CurveFamily::Gompertz) => "Гомпертц",
298302
(UiLanguage::Russian, CurveFamily::Lorentzian) => "Лоренциан",
299303
(UiLanguage::Russian, CurveFamily::NaturalLog) => "Натуральный логарифм",
300304
(UiLanguage::Russian, CurveFamily::FourPl) => "4PL",

src/app/normalization.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,6 @@ pub(super) struct ParametricNormalization {
1616
y_scale: f64,
1717
}
1818

19-
fn is_polynomial_family(family: CurveFamily) -> bool {
20-
matches!(
21-
family,
22-
CurveFamily::Linear
23-
| CurveFamily::Quadratic
24-
| CurveFamily::Cubic
25-
| CurveFamily::Quartic
26-
| CurveFamily::Quintic
27-
| CurveFamily::Sextic
28-
| CurveFamily::Septic
29-
| CurveFamily::Octic
30-
| CurveFamily::Nonic
31-
)
32-
}
33-
3419
impl ParametricNormalization {
3520
/// Строит коэффициенты нормализации по максимальным абсолютным значениям `x` и `y`.
3621
pub(super) fn try_from_points(points: &Points) -> Result<Self, String> {
@@ -85,7 +70,7 @@ impl ParametricNormalization {
8570
let x_scale = self.x_scale;
8671
let y_scale = self.y_scale;
8772

88-
if is_polynomial_family(family) {
73+
if family.is_polynomial() {
8974
let degree = values.len().saturating_sub(1);
9075
for (index, value) in values.iter_mut().enumerate() {
9176
let power = (degree - index) as i32;
@@ -127,6 +112,17 @@ impl ParametricNormalization {
127112
values[2] *= x_scale;
128113
}
129114
}
115+
CurveFamily::Gompertz => {
116+
if to_normalized {
117+
values[0] /= y_scale;
118+
values[1] *= x_scale;
119+
values[2] /= x_scale;
120+
} else {
121+
values[0] *= y_scale;
122+
values[1] /= x_scale;
123+
values[2] *= x_scale;
124+
}
125+
}
130126
CurveFamily::Lorentzian => {
131127
if to_normalized {
132128
values[0] /= y_scale;

src/app/param_init.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,12 @@ pub(super) fn polynomial_family(degree: usize) -> CurveFamily {
1616
}
1717
}
1818

19-
fn is_polynomial_family(family: CurveFamily) -> bool {
20-
matches!(
21-
family,
22-
CurveFamily::Linear
23-
| CurveFamily::Quadratic
24-
| CurveFamily::Cubic
25-
| CurveFamily::Quartic
26-
| CurveFamily::Quintic
27-
| CurveFamily::Sextic
28-
| CurveFamily::Septic
29-
| CurveFamily::Octic
30-
| CurveFamily::Nonic
31-
)
32-
}
33-
3419
pub(super) fn is_advanced_param_init_supported(family: CurveFamily) -> bool {
35-
is_polynomial_family(family)
20+
family.is_polynomial()
3621
|| matches!(
3722
family,
3823
CurveFamily::Logistic
24+
| CurveFamily::Gompertz
3925
| CurveFamily::Gaussian
4026
| CurveFamily::ExponentialBasic
4127
| CurveFamily::Power
@@ -49,12 +35,13 @@ pub(super) fn data_based_params_for_family(
4935
family: CurveFamily,
5036
points: &Points,
5137
) -> Result<CurveParams, String> {
52-
if is_polynomial_family(family) {
38+
if family.is_polynomial() {
5339
return data_based_polynomial_params(family, points);
5440
}
5541

5642
match family {
5743
CurveFamily::Logistic => data_based_logistic_params(points),
44+
CurveFamily::Gompertz => data_based_gompertz_params(points),
5845
CurveFamily::Gaussian => data_based_gaussian_params(points),
5946
CurveFamily::ExponentialBasic => data_based_exponential_basic_params(points),
6047
CurveFamily::Power => data_based_power_params(points),
@@ -77,13 +64,15 @@ fn data_based_polynomial_params(
7764
}
7865

7966
fn data_based_logistic_params(points: &Points) -> Result<CurveParams, String> {
80-
let (x_min, x_max, _, y_max, _) = point_extrema(points);
81-
let x_span = (x_max - x_min).max(PARAM_INIT_SPAN_EPS);
82-
CurveParams::try_from_values(
83-
CurveFamily::Logistic,
84-
vec![y_max, 4.0 / x_span, (x_min + x_max) * 0.5],
85-
)
86-
.map_err(|error| error.to_string())
67+
let (a, b, c) = data_based_sigmoid_abc(points);
68+
CurveParams::try_from_values(CurveFamily::Logistic, vec![a, b, c])
69+
.map_err(|error| error.to_string())
70+
}
71+
72+
fn data_based_gompertz_params(points: &Points) -> Result<CurveParams, String> {
73+
let (a, b, c) = data_based_sigmoid_abc(points);
74+
CurveParams::try_from_values(CurveFamily::Gompertz, vec![a, b, c])
75+
.map_err(|error| error.to_string())
8776
}
8877

8978
fn data_based_gaussian_params(points: &Points) -> Result<CurveParams, String> {
@@ -141,6 +130,12 @@ fn data_based_power_params(points: &Points) -> Result<CurveParams, String> {
141130
.map_err(|error| error.to_string())
142131
}
143132

133+
fn data_based_sigmoid_abc(points: &Points) -> (f64, f64, f64) {
134+
let (x_min, x_max, _, y_max, _) = point_extrema(points);
135+
let x_span = (x_max - x_min).max(PARAM_INIT_SPAN_EPS);
136+
(y_max, 4.0 / x_span, (x_min + x_max) * 0.5)
137+
}
138+
144139
fn linear_regression(points: &Points) -> Result<(f64, f64), String> {
145140
if points.len() < 2 {
146141
return Err("Linear regression requires at least two points".to_string());

0 commit comments

Comments
 (0)