Skip to content

Commit 175ff2f

Browse files
committed
added after rounding / quantization for metrics
1 parent 8919c72 commit 175ff2f

9 files changed

Lines changed: 780 additions & 224 deletions

File tree

src/app.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,23 @@ use crate::domain::{
6464
};
6565
use crate::fit::IterationMetricSnapshot;
6666
use crate::fit::OptimizationLossMetric;
67+
use crate::fit::{
68+
DEFAULT_METRIC_QUANTIZATION_DECIMAL_PLACES, MetricQuantization,
69+
MetricQuantizationDecimalPlaces, SplineConfig, SplineDuplicateXPolicy, SplineExtrapolation,
70+
SplineFamilyKind, SplineKnotStrategy, SplineResult, build_spline_initial_curve_from_knot_y,
71+
calculate_iteration_metrics_with_quantization, calculate_metrics_with_quantization,
72+
default_spline_initial_knot_y, sample_curve,
73+
};
6774
#[cfg(not(target_arch = "wasm32"))]
68-
use crate::fit::{FitError, fit_curve_with_progress_and_optimizer_config_and_loss_metric};
75+
use crate::fit::{
76+
FitError, fit_curve_with_progress_and_optimizer_config_and_loss_metric_and_metric_quantization,
77+
};
6978
#[cfg(target_arch = "wasm32")]
7079
use crate::fit::{
7180
IncrementalFitRunner, IncrementalFitStep, IncrementalSplineFitRunner, IncrementalSplineFitStep,
7281
};
7382
#[cfg(not(target_arch = "wasm32"))]
7483
use crate::fit::{IncrementalSplineFitRunner, IncrementalSplineFitStep};
75-
use crate::fit::{
76-
SplineConfig, SplineDuplicateXPolicy, SplineExtrapolation, SplineFamilyKind,
77-
SplineKnotStrategy, SplineResult, build_spline_initial_curve_from_knot_y,
78-
calculate_iteration_metrics, calculate_metrics, default_spline_initial_knot_y, sample_curve,
79-
};
8084

8185
#[cfg(not(target_arch = "wasm32"))]
8286
use std::sync::atomic::{AtomicBool, Ordering};
@@ -644,7 +648,10 @@ enum FitWorkerMessage {
644648
},
645649
Stopped,
646650
Finished(FitResult),
647-
SplineFinished(SplineResult),
651+
SplineFinished {
652+
result: SplineResult,
653+
metrics: IterationMetricSnapshot,
654+
},
648655
Failed(String),
649656
}
650657

@@ -672,6 +679,8 @@ pub struct CurveFitApp {
672679
optimizer_method: OptimizerMethod,
673680
optimizer_mode: OptimizerUiMode,
674681
optimization_loss_metric: OptimizationLossMetric,
682+
metric_quantization_enabled: bool,
683+
metric_quantization_decimal_places: u8,
675684
normalize_parametric_data: bool,
676685
lbfgs_inputs: LbfgsInputState,
677686
lbfgs_preset: OptimizerPreset,
@@ -708,6 +717,7 @@ pub struct CurveFitApp {
708717
spline_initial_knot_y_inputs: Vec<String>,
709718
fit_in_progress: bool,
710719
fit_loss_metric: OptimizationLossMetric,
720+
fit_metric_quantization: MetricQuantization,
711721
fit_preview_params: Option<CurveParams>,
712722
fit_preview_iteration: Option<u64>,
713723
fit_result: Option<FitResult>,
@@ -927,6 +937,13 @@ impl CurveFitApp {
927937
self.spline_initial_knot_y_inputs = values.iter().map(|value| value.to_string()).collect();
928938
}
929939

940+
fn selected_metric_quantization(&self) -> Result<MetricQuantization, String> {
941+
MetricQuantization::from_ui_state(
942+
self.metric_quantization_enabled,
943+
self.metric_quantization_decimal_places,
944+
)
945+
}
946+
930947
fn clear_fit_outputs(&mut self) {
931948
self.cancel_fit_and_discard_updates();
932949
self.fit_result = None;
@@ -1245,6 +1262,8 @@ impl Default for CurveFitApp {
12451262
optimizer_method: OptimizerMethod::Lbfgs,
12461263
optimizer_mode: OptimizerUiMode::Basic,
12471264
optimization_loss_metric: OptimizationLossMetric::default(),
1265+
metric_quantization_enabled: false,
1266+
metric_quantization_decimal_places: DEFAULT_METRIC_QUANTIZATION_DECIMAL_PLACES,
12481267
normalize_parametric_data: false,
12491268
lbfgs_inputs: LbfgsInputState::from_config(&default_lbfgs),
12501269
lbfgs_preset: infer_lbfgs_preset(&default_lbfgs),
@@ -1283,6 +1302,7 @@ impl Default for CurveFitApp {
12831302
replay: ReplayState::default(),
12841303
fit_in_progress: false,
12851304
fit_loss_metric: OptimizationLossMetric::default(),
1305+
fit_metric_quantization: MetricQuantization::Disabled,
12861306
fit_preview_params: None,
12871307
fit_preview_iteration: None,
12881308
fit_result: None,

src/app/diagnostics.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ impl IterationDiagnostics {
3030
points: &Points,
3131
params: &CurveParams,
3232
loss_metric: OptimizationLossMetric,
33+
metric_quantization: MetricQuantization,
3334
) {
3435
let family = params.family();
3536
self.reset_for_family(family);
36-
let metrics = calculate_iteration_metrics(points, params, loss_metric);
37+
let metrics = calculate_iteration_metrics_with_quantization(
38+
points,
39+
params,
40+
loss_metric,
41+
metric_quantization,
42+
);
3743
self.append(0, metrics, params);
3844
}
3945

src/app/fit_worker.rs

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct ParametricFitWorkerInput {
99
normalization: Option<ParametricNormalization>,
1010
optimizer_config: OptimizerConfig,
1111
loss_metric: OptimizationLossMetric,
12+
metric_quantization: MetricQuantization,
1213
cancel_flag: Arc<AtomicBool>,
1314
}
1415

@@ -48,47 +49,27 @@ impl CurveFitApp {
4849
points: &Points,
4950
params: &CurveParams,
5051
) {
51-
let points_slice = points.as_slice();
52-
let sample_count = points_slice.len() as f64;
53-
let y_mean = points_slice.iter().map(|point| point.y()).sum::<f64>() / sample_count;
52+
let metrics = calculate_iteration_metrics_with_quantization(
53+
points,
54+
params,
55+
self.fit_loss_metric,
56+
self.fit_metric_quantization,
57+
);
58+
self.result_metrics = Some(ExtendedMetrics {
59+
mse: metrics.mse,
60+
rmse: metrics.rmse,
61+
mae: metrics.mae,
62+
r2: metrics.r2,
63+
max_abs_error: metrics.max_abs_error,
64+
});
5465

55-
let mut sse = 0.0;
56-
let mut sae = 0.0;
57-
let mut max_abs_error = 0.0_f64;
5866
self.residual_plot_points.clear();
59-
self.residual_plot_points.reserve(points_slice.len());
60-
for point in points_slice {
67+
self.residual_plot_points.reserve(points.len());
68+
for point in points.as_slice() {
6169
let residual = params.evaluate(point.x()) - point.y();
62-
let abs_residual = residual.abs();
63-
sse += residual * residual;
64-
sae += abs_residual;
65-
max_abs_error = max_abs_error.max(abs_residual);
6670
self.residual_plot_points
6771
.push(PlotPoint::new(point.x(), residual));
6872
}
69-
70-
let sst = points_slice
71-
.iter()
72-
.map(|point| {
73-
let centered = point.y() - y_mean;
74-
centered * centered
75-
})
76-
.sum::<f64>();
77-
let mse = sse / sample_count;
78-
let rmse = mse.sqrt();
79-
let mae = sae / sample_count;
80-
let r2 = if sst <= 1e-15 {
81-
if sse <= 1e-15 { 1.0 } else { 0.0 }
82-
} else {
83-
1.0 - sse / sst
84-
};
85-
self.result_metrics = Some(ExtendedMetrics {
86-
mse,
87-
rmse,
88-
mae,
89-
r2,
90-
max_abs_error,
91-
});
9273
}
9374

9475
pub(super) fn update_spline_result_metrics(&mut self, result: &SplineResult) {
@@ -171,10 +152,11 @@ impl CurveFitApp {
171152
if !self.discard_fit_worker_updates {
172153
if let Some(points) = fit_points.as_ref() {
173154
self.update_parametric_result_metrics(points, &result.params);
174-
let metrics = calculate_iteration_metrics(
155+
let metrics = calculate_iteration_metrics_with_quantization(
175156
points,
176157
&result.params,
177158
self.fit_loss_metric,
159+
self.fit_metric_quantization,
178160
);
179161
self.iteration_diagnostics.append(
180162
result.iterations,
@@ -195,14 +177,13 @@ impl CurveFitApp {
195177
keep_receiver = false;
196178
break;
197179
}
198-
Ok(FitWorkerMessage::SplineFinished(result)) => {
180+
Ok(FitWorkerMessage::SplineFinished { result, metrics }) => {
199181
self.fit_in_progress = false;
200182
if !self.discard_fit_worker_updates {
201183
let knot_y = result.knots.iter().map(|knot| knot[1]).collect::<Vec<_>>();
202184
let spline_plot_curve =
203185
Self::plot_points_from_pairs(result.curve.iter().copied());
204186
self.update_spline_result_metrics(&result);
205-
let metrics = result.iteration_metrics_snapshot(self.fit_loss_metric);
206187
self.iteration_diagnostics.append_spline(
207188
result.iterations,
208189
metrics,
@@ -343,7 +324,12 @@ impl CurveFitApp {
343324
params
344325
};
345326
let metrics = if let Some(points) = self.active_fit_points.as_ref() {
346-
calculate_iteration_metrics(points, &params, self.fit_loss_metric)
327+
calculate_iteration_metrics_with_quantization(
328+
points,
329+
&params,
330+
self.fit_loss_metric,
331+
self.fit_metric_quantization,
332+
)
347333
} else {
348334
metrics
349335
};
@@ -367,14 +353,19 @@ impl CurveFitApp {
367353
}
368354
let fit_points = self.active_fit_points.take();
369355
if let Some(points) = fit_points.as_ref() {
370-
let (mse, rmse) = calculate_metrics(points, &result.params);
356+
let (mse, rmse) = calculate_metrics_with_quantization(
357+
points,
358+
&result.params,
359+
self.fit_metric_quantization,
360+
);
371361
result.mse = mse;
372362
result.rmse = rmse;
373363
self.update_parametric_result_metrics(points, &result.params);
374-
let metrics = calculate_iteration_metrics(
364+
let metrics = calculate_iteration_metrics_with_quantization(
375365
points,
376366
&result.params,
377367
self.fit_loss_metric,
368+
self.fit_metric_quantization,
378369
);
379370
self.iteration_diagnostics.append(
380371
result.iterations,
@@ -421,13 +412,12 @@ impl CurveFitApp {
421412
self.upsert_spline_replay_frame(iteration, Self::plot_points_from_pairs(curve));
422413
self.status = Some(StatusMessage::FittingInProgress);
423414
}
424-
Ok(IncrementalSplineFitStep::Finished(result)) => {
415+
Ok(IncrementalSplineFitStep::Finished { result, metrics }) => {
425416
self.fit_in_progress = false;
426417
let knot_y = result.knots.iter().map(|knot| knot[1]).collect::<Vec<_>>();
427418
let spline_plot_curve =
428419
Self::plot_points_from_pairs(result.curve.iter().copied());
429420
self.update_spline_result_metrics(&result);
430-
let metrics = result.iteration_metrics_snapshot(self.fit_loss_metric);
431421
self.iteration_diagnostics
432422
.append_spline(result.iterations, metrics, &knot_y);
433423
self.upsert_spline_replay_frame(result.iterations, spline_plot_curve);
@@ -464,6 +454,7 @@ impl CurveFitApp {
464454
normalization,
465455
optimizer_config,
466456
loss_metric,
457+
metric_quantization,
467458
cancel_flag,
468459
} = input;
469460
let (tx, rx) = mpsc::channel();
@@ -477,12 +468,14 @@ impl CurveFitApp {
477468
let progress_cancel = cancel_flag.clone();
478469
let progress_points = Arc::new(display_points);
479470
let callback_points = Arc::clone(&progress_points);
480-
let result = fit_curve_with_progress_and_optimizer_config_and_loss_metric(
471+
let result =
472+
fit_curve_with_progress_and_optimizer_config_and_loss_metric_and_metric_quantization(
481473
&optimization_points,
482474
family,
483475
optimization_initial_params,
484476
&optimizer_config,
485477
loss_metric,
478+
metric_quantization,
486479
move |iteration, params| {
487480
if progress_cancel.load(Ordering::Relaxed) {
488481
return false;
@@ -496,10 +489,11 @@ impl CurveFitApp {
496489
} else {
497490
params
498491
};
499-
let metrics = calculate_iteration_metrics(
492+
let metrics = calculate_iteration_metrics_with_quantization(
500493
callback_points.as_ref(),
501494
&params,
502495
loss_metric,
496+
metric_quantization,
503497
);
504498
let _ = iter_tx.send(FitWorkerMessage::Iteration {
505499
iteration,
@@ -524,7 +518,11 @@ impl CurveFitApp {
524518
} else {
525519
result.params
526520
};
527-
let (mse, rmse) = calculate_metrics(progress_points.as_ref(), &params);
521+
let (mse, rmse) = calculate_metrics_with_quantization(
522+
progress_points.as_ref(),
523+
&params,
524+
metric_quantization,
525+
);
528526
let _ = tx.send(FitWorkerMessage::Finished(FitResult {
529527
family: result.family,
530528
params,
@@ -554,6 +552,7 @@ impl CurveFitApp {
554552
cancel_flag: Arc<AtomicBool>,
555553
) {
556554
let loss_metric = self.fit_loss_metric;
555+
let metric_quantization = self.fit_metric_quantization;
557556
let (tx, rx) = mpsc::channel();
558557
self.fit_worker_rx = Some(rx);
559558
self.fit_cancel_flag = Some(cancel_flag.clone());
@@ -569,6 +568,7 @@ impl CurveFitApp {
569568
&optimizer_config,
570569
Some(initial_knot_y.as_slice()),
571570
loss_metric,
571+
metric_quantization,
572572
) {
573573
Ok(runner) => runner,
574574
Err(error) => {
@@ -597,8 +597,8 @@ impl CurveFitApp {
597597
curve,
598598
});
599599
}
600-
Ok(IncrementalSplineFitStep::Finished(result)) => {
601-
let _ = tx.send(FitWorkerMessage::SplineFinished(result));
600+
Ok(IncrementalSplineFitStep::Finished { result, metrics }) => {
601+
let _ = tx.send(FitWorkerMessage::SplineFinished { result, metrics });
602602
break;
603603
}
604604
Ok(IncrementalSplineFitStep::Cancelled) | Err(FitError::Cancelled) => {
@@ -635,6 +635,14 @@ impl CurveFitApp {
635635
};
636636
let loss_metric = self.optimization_loss_metric;
637637
self.fit_loss_metric = loss_metric;
638+
let metric_quantization = match self.selected_metric_quantization() {
639+
Ok(metric_quantization) => metric_quantization,
640+
Err(error) => {
641+
self.status = Some(StatusMessage::Error(error));
642+
return;
643+
}
644+
};
645+
self.fit_metric_quantization = metric_quantization;
638646

639647
let selected_model = self.resolved_model();
640648
if let Some(spline_family) = selected_model.spline_family() {
@@ -687,6 +695,7 @@ impl CurveFitApp {
687695
&optimizer_config,
688696
Some(initial_knot_y.as_slice()),
689697
loss_metric,
698+
metric_quantization,
690699
) {
691700
Ok(runner) => {
692701
self.wasm_fit_job =
@@ -753,8 +762,12 @@ impl CurveFitApp {
753762
// Очищаем предыдущий успешный результат только когда новый запуск уже валиден.
754763
self.clear_fit_outputs();
755764
self.active_fit_points = Some(points.clone());
756-
self.iteration_diagnostics
757-
.initialize(&points, &initial_params, loss_metric);
765+
self.iteration_diagnostics.initialize(
766+
&points,
767+
&initial_params,
768+
loss_metric,
769+
metric_quantization,
770+
);
758771
self.upsert_parametric_replay_frame(0, initial_params.clone());
759772
self.status = Some(StatusMessage::FittingInProgress);
760773

@@ -769,18 +782,20 @@ impl CurveFitApp {
769782
normalization,
770783
optimizer_config,
771784
loss_metric,
785+
metric_quantization,
772786
cancel_flag,
773787
});
774788
}
775789

776790
#[cfg(target_arch = "wasm32")]
777791
{
778-
match IncrementalFitRunner::new_with_optimizer_config_and_loss_metric(
792+
match IncrementalFitRunner::new_with_optimizer_config_and_loss_metric_and_metric_quantization(
779793
&optimization_points,
780794
family,
781795
optimization_initial_params,
782796
&optimizer_config,
783797
loss_metric,
798+
metric_quantization,
784799
) {
785800
Ok(runner) => {
786801
self.wasm_fit_job = Some(WasmFitJob::Deferred(WasmFitRunner::Parametric {

0 commit comments

Comments
 (0)