Skip to content

Commit 40cfae4

Browse files
committed
refactoring
1 parent dda0294 commit 40cfae4

24 files changed

Lines changed: 1038 additions & 943 deletions

src/app/diagnostics.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,17 @@ impl IterationDiagnostics {
5656
self.reset_for_family(family);
5757
}
5858

59-
let values = params.values();
60-
if values.len() != self.parameter_series.len() {
59+
if family.parameter_count() != self.parameter_series.len() {
6160
self.reset_for_family(family);
6261
}
6362

6463
let iteration = iteration as f64;
6564
self.upsert_metrics(iteration, metrics);
66-
for (series, value) in self.parameter_series.iter_mut().zip(values) {
67-
upsert_iteration_point(series, iteration, value);
68-
}
65+
params.with_values(|values| {
66+
for (series, value) in self.parameter_series.iter_mut().zip(values.iter().copied()) {
67+
upsert_iteration_point(series, iteration, value);
68+
}
69+
});
6970
}
7071

7172
pub(super) fn append_spline(

src/app/fit_worker.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,10 @@ impl CurveFitApp {
163163
r2: metrics.r2,
164164
max_abs_error: metrics.max_abs_error,
165165
};
166-
let mut residual_plot_points = Vec::with_capacity(points.len());
167-
for point in points.as_slice() {
168-
let residual = params.evaluate(point.x()) - point.y();
169-
residual_plot_points.push(PlotPoint::new(point.x(), residual));
170-
}
166+
let residual_plot_points = points
167+
.iter()
168+
.map(|point| PlotPoint::new(point.x(), params.evaluate(point.x()) - point.y()))
169+
.collect();
171170

172171
(metrics, result_metrics, residual_plot_points)
173172
}

src/app/input_parse.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
33
use super::*;
44

5+
fn parse_indexed_f64_inputs(inputs: &[String], field_prefix: &str) -> Result<Vec<f64>, String> {
6+
inputs
7+
.iter()
8+
.enumerate()
9+
.map(|(index, raw_value)| parse_f64(&format!("{field_prefix}[{index}]"), raw_value))
10+
.collect()
11+
}
12+
513
/// Уже распарсенные и типизированные начальные параметры параметрической модели.
614
#[derive(Debug, Clone)]
715
pub(super) struct ParsedInitialParams(CurveParams);
@@ -20,11 +28,7 @@ impl ParsedInitialParams {
2028
));
2129
}
2230

23-
let mut values = Vec::with_capacity(expected_count);
24-
for (index, raw_value) in inputs.iter().enumerate() {
25-
let field = format!("parameter[{index}]");
26-
values.push(parse_f64(&field, raw_value)?);
27-
}
31+
let values = parse_indexed_f64_inputs(inputs, "parameter")?;
2832

2933
let params =
3034
CurveParams::try_from_slice_with_tau_grid(family, &values, saturating_trend_tau_grid)
@@ -50,11 +54,7 @@ impl ParsedSaturatingTrendTauGrid {
5054
));
5155
}
5256

53-
let mut values = Vec::with_capacity(expected_count);
54-
for (index, raw_value) in inputs.iter().take(expected_count).enumerate() {
55-
let field = format!("tau[{index}]");
56-
values.push(parse_f64(&field, raw_value)?);
57-
}
57+
let values = parse_indexed_f64_inputs(&inputs[..expected_count], "tau")?;
5858

5959
let grid =
6060
SaturatingTrendTauGrid::from_values(&values).map_err(|error| error.to_string())?;
@@ -81,13 +81,9 @@ impl ParsedSplineInitialKnotY {
8181
));
8282
}
8383

84-
let mut values = Vec::with_capacity(expected_count);
85-
for (index, raw_value) in inputs.iter().enumerate() {
86-
let field = format!("spline_knot_y[{index}]");
87-
values.push(parse_f64(&field, raw_value)?);
88-
}
89-
90-
Ok(Self { values })
84+
Ok(Self {
85+
values: parse_indexed_f64_inputs(inputs, "spline_knot_y")?,
86+
})
9187
}
9288

9389
pub(super) fn as_slice(&self) -> &[f64] {

src/app/normalization.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,9 @@ pub(super) struct ParametricNormalization {
5555
impl ParametricNormalization {
5656
/// Строит коэффициенты нормализации по максимальным абсолютным значениям `x` и `y`.
5757
pub(super) fn try_from_points(points: &Points) -> Result<Self, String> {
58-
let mut max_abs_x = 0.0_f64;
59-
let mut max_abs_y = 0.0_f64;
60-
for point in points.as_slice() {
61-
max_abs_x = max_abs_x.max(point.x().abs());
62-
max_abs_y = max_abs_y.max(point.y().abs());
63-
}
58+
let (max_abs_x, max_abs_y) = points.iter().fold((0.0_f64, 0.0_f64), |acc, point| {
59+
(acc.0.max(point.x().abs()), acc.1.max(point.y().abs()))
60+
});
6461

6562
let x_scale = max_abs_x.max(NORMALIZATION_SCALE_EPS);
6663
let y_scale = max_abs_y.max(NORMALIZATION_SCALE_EPS);
@@ -73,14 +70,13 @@ impl ParametricNormalization {
7370

7471
/// Нормализует точки для внутреннего фиттинга.
7572
pub(super) fn normalize_points(self, points: &Points) -> Result<Points, String> {
76-
let mut normalized = Vec::with_capacity(points.len());
77-
for point in points.as_slice() {
78-
let x = point.x() / self.x_scale;
79-
let y = point.y() / self.y_scale;
80-
let normalized_point = Point::try_new(x, y)
81-
.map_err(|error| format!("Normalized point must be finite: {error}"))?;
82-
normalized.push(normalized_point);
83-
}
73+
let normalized = points
74+
.iter()
75+
.map(|point| {
76+
Point::try_new(point.x() / self.x_scale, point.y() / self.y_scale)
77+
.map_err(|error| format!("Normalized point must be finite: {error}"))
78+
})
79+
.collect::<Result<Vec<_>, _>>()?;
8480

8581
Points::try_from(normalized)
8682
.map_err(|error| format!("Normalized points are invalid: {error}"))

src/app/optimizer.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::domain::{
44
AdamConfig, LbfgsConfig, NelderMeadConfig, NewtonCgConfig, OptimizerMethod, SgdConfig,
55
SteepestDescentConfig,
66
};
7+
use std::hash::{Hash, Hasher};
78

89
use super::{C1_MIN, C2_MAX, STEP_MAX_MAX, STEP_MIN_MIN, UiLanguage};
910

@@ -88,6 +89,30 @@ fn normalize_wolfe_line_search_inputs(
8889
*step_max = (*step_max).clamp(*step_min + 1e-6, STEP_MAX_MAX);
8990
}
9091

92+
fn hash_f64_normalized<H: Hasher>(hasher: &mut H, value: f64) {
93+
let normalized_bits = if value == 0.0 {
94+
0.0f64.to_bits()
95+
} else {
96+
value.to_bits()
97+
};
98+
normalized_bits.hash(hasher);
99+
}
100+
101+
fn hash_wolfe_line_search_inputs<H: Hasher>(
102+
hasher: &mut H,
103+
c1: f64,
104+
c2: f64,
105+
step_min: f64,
106+
step_max: f64,
107+
width_tolerance: f64,
108+
) {
109+
hash_f64_normalized(hasher, c1);
110+
hash_f64_normalized(hasher, c2);
111+
hash_f64_normalized(hasher, step_min);
112+
hash_f64_normalized(hasher, step_max);
113+
hash_f64_normalized(hasher, width_tolerance);
114+
}
115+
91116
pub(super) fn lbfgs_config_from_preset(preset: OptimizerPreset) -> LbfgsConfig {
92117
match preset {
93118
OptimizerPreset::Fast => {
@@ -247,6 +272,21 @@ impl LbfgsInputState {
247272
)
248273
.map_err(|error| error.to_string())
249274
}
275+
276+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
277+
self.history_size.hash(hasher);
278+
self.max_iters.hash(hasher);
279+
hash_f64_normalized(hasher, self.tol_grad);
280+
hash_f64_normalized(hasher, self.tol_cost);
281+
hash_wolfe_line_search_inputs(
282+
hasher,
283+
self.c1,
284+
self.c2,
285+
self.step_min,
286+
self.step_max,
287+
self.width_tolerance,
288+
);
289+
}
250290
}
251291

252292
#[derive(Debug, Clone, PartialEq)]
@@ -294,6 +334,16 @@ impl NelderMeadInputState {
294334
)
295335
.map_err(|error| error.to_string())
296336
}
337+
338+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
339+
self.max_iters.hash(hasher);
340+
hash_f64_normalized(hasher, self.simplex_scale);
341+
hash_f64_normalized(hasher, self.sd_tolerance);
342+
hash_f64_normalized(hasher, self.alpha);
343+
hash_f64_normalized(hasher, self.gamma);
344+
hash_f64_normalized(hasher, self.rho);
345+
hash_f64_normalized(hasher, self.sigma);
346+
}
297347
}
298348

299349
#[derive(Debug, Clone, PartialEq)]
@@ -338,6 +388,18 @@ impl SteepestDescentInputState {
338388
)
339389
.map_err(|error| error.to_string())
340390
}
391+
392+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
393+
self.max_iters.hash(hasher);
394+
hash_wolfe_line_search_inputs(
395+
hasher,
396+
self.c1,
397+
self.c2,
398+
self.step_min,
399+
self.step_max,
400+
self.width_tolerance,
401+
);
402+
}
341403
}
342404

343405
#[derive(Debug, Clone, PartialEq)]
@@ -391,6 +453,20 @@ impl NewtonCgInputState {
391453
)
392454
.map_err(|error| error.to_string())
393455
}
456+
457+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
458+
self.max_iters.hash(hasher);
459+
hash_f64_normalized(hasher, self.tol);
460+
hash_f64_normalized(hasher, self.curvature_threshold);
461+
hash_wolfe_line_search_inputs(
462+
hasher,
463+
self.c1,
464+
self.c2,
465+
self.step_min,
466+
self.step_max,
467+
self.width_tolerance,
468+
);
469+
}
394470
}
395471

396472
#[derive(Debug, Clone, PartialEq)]
@@ -414,6 +490,11 @@ impl SgdInputState {
414490
pub(super) fn to_config(&self) -> Result<SgdConfig, String> {
415491
SgdConfig::try_new(self.max_iters, self.learning_rate).map_err(|error| error.to_string())
416492
}
493+
494+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
495+
self.max_iters.hash(hasher);
496+
hash_f64_normalized(hasher, self.learning_rate);
497+
}
417498
}
418499

419500
#[derive(Debug, Clone, PartialEq)]
@@ -437,4 +518,9 @@ impl AdamInputState {
437518
pub(super) fn to_config(&self) -> Result<AdamConfig, String> {
438519
AdamConfig::try_new(self.max_iters, self.learning_rate).map_err(|error| error.to_string())
439520
}
521+
522+
pub(super) fn hash_into<H: Hasher>(&self, hasher: &mut H) {
523+
self.max_iters.hash(hasher);
524+
hash_f64_normalized(hasher, self.learning_rate);
525+
}
440526
}

0 commit comments

Comments
 (0)