Skip to content

Commit 6c56eec

Browse files
committed
code polishing
1 parent ef5150c commit 6c56eec

6 files changed

Lines changed: 232 additions & 157 deletions

File tree

src/app/fit_worker.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use super::*;
88
struct ParametricFitWorkerInput {
99
family: CurveFamily,
1010
optimization_points: Points,
11-
display_points: Points,
11+
display_points: Option<Points>,
1212
optimization_initial_params: CurveParams,
1313
normalization: Option<ParametricNormalization>,
1414
optimizer_config: OptimizerConfig,
@@ -588,7 +588,7 @@ impl CurveFitApp {
588588
self.fit_in_progress = true;
589589

590590
std::thread::spawn(move || {
591-
let progress_points = display_points;
591+
let progress_points = display_points.as_ref().unwrap_or(&optimization_points);
592592
let mut iteration_trace = Vec::new();
593593
let mut runner = match IncrementalFitRunner::new_with_optimizer_config_and_loss_metric_and_metric_quantization(
594594
&optimization_points,
@@ -626,7 +626,7 @@ impl CurveFitApp {
626626
params
627627
};
628628
let metrics = calculate_iteration_metrics_with_quantization(
629-
&progress_points,
629+
progress_points,
630630
&params,
631631
loss_metric,
632632
metric_quantization,
@@ -650,7 +650,7 @@ impl CurveFitApp {
650650
result.params
651651
};
652652
let (mse, rmse) = calculate_metrics_with_quantization(
653-
&progress_points,
653+
progress_points,
654654
&params,
655655
metric_quantization,
656656
);
@@ -886,39 +886,52 @@ impl CurveFitApp {
886886
return;
887887
}
888888

889-
let mut optimization_points = points.clone();
890-
let mut optimization_initial_params = initial_params.clone();
891-
let normalization = if self.normalize_parametric_data {
889+
let fit_seed_initial_params = initial_params.clone();
890+
let (
891+
optimization_points,
892+
display_points,
893+
active_fit_points,
894+
optimization_initial_params,
895+
normalization,
896+
) = if self.normalize_parametric_data {
892897
let normalization = match ParametricNormalization::try_from_points(&points) {
893898
Ok(normalization) => normalization,
894899
Err(error) => {
895900
self.status = Some(StatusMessage::Error(error));
896901
return;
897902
}
898903
};
899-
optimization_points = match normalization.normalize_points(&points) {
904+
let normalized_points = match normalization.normalize_points(&points) {
900905
Ok(normalized_points) => normalized_points,
901906
Err(error) => {
902907
self.status = Some(StatusMessage::Error(error));
903908
return;
904909
}
905910
};
906-
optimization_initial_params = match normalization.normalize_params(&initial_params) {
911+
let normalized_initial_params = match normalization.normalize_params(&initial_params) {
907912
Ok(normalized_params) => normalized_params,
908913
Err(error) => {
909914
self.status = Some(StatusMessage::Error(error));
910915
return;
911916
}
912917
};
913-
Some(normalization)
918+
919+
(
920+
normalized_points,
921+
Some(points.clone()),
922+
points,
923+
normalized_initial_params,
924+
Some(normalization),
925+
)
914926
} else {
915-
None
927+
let active_fit_points = points.clone();
928+
(points, None, active_fit_points, initial_params, None)
916929
};
917930

918931
self.reset_fit_runtime_for_new_run();
919-
self.active_fit_points = Some(points.clone());
932+
self.active_fit_points = Some(active_fit_points);
920933
self.fit_run_ui_seed = Some(FitRunUiSeed::Parametric {
921-
initial_params: initial_params.clone(),
934+
initial_params: fit_seed_initial_params,
922935
});
923936
self.start_fit_timer();
924937
self.status = Some(StatusMessage::FittingInProgress);
@@ -929,7 +942,7 @@ impl CurveFitApp {
929942
self.start_fit_worker(ParametricFitWorkerInput {
930943
family,
931944
optimization_points,
932-
display_points: points,
945+
display_points,
933946
optimization_initial_params,
934947
normalization,
935948
optimizer_config,

src/app/state.rs

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Центральные типы состояния приложения, снимки UI и сообщения рантайма фитинга.
22
33
use super::*;
4+
use std::hash::{DefaultHasher, Hash, Hasher};
45

56
/// Неизменяемое представление выбранного оптимизатора и его input-состояния.
67
pub(super) enum ActiveOptimizerView<'a> {
@@ -128,34 +129,8 @@ impl ActiveOptimizerViewMut<'_> {
128129
}
129130
}
130131

131-
#[derive(Debug, Clone, PartialEq)]
132-
/// Снимок активных input-полей оптимизатора для детекта изменений UI.
133-
pub(super) enum ActiveOptimizerSnapshot {
134-
Lbfgs(LbfgsInputState),
135-
NelderMead(NelderMeadInputState),
136-
SteepestDescent(SteepestDescentInputState),
137-
NewtonCg(NewtonCgInputState),
138-
Sgd(SgdInputState),
139-
Adam(AdamInputState),
140-
}
141-
142-
#[derive(Debug, Clone, PartialEq)]
143-
/// Снимок правой панели, влияющий на автоперезапуск фитинга.
144-
pub(super) struct RightPanelFitSnapshot {
145-
pub(super) selected_model: ModelChoice,
146-
pub(super) polynomial_degree: usize,
147-
pub(super) rational_degree: usize,
148-
pub(super) parameter_inputs: Vec<String>,
149-
pub(super) spline_knots: usize,
150-
pub(super) spline_knot_strategy: SplineKnotStrategy,
151-
pub(super) spline_extrapolation: SplineExtrapolation,
152-
pub(super) spline_duplicate_x_policy: SplineDuplicateXPolicy,
153-
pub(super) spline_initial_knot_y_inputs: Vec<String>,
154-
pub(super) optimization_loss_metric: OptimizationLossMetric,
155-
pub(super) metric_quantization_enabled: bool,
156-
pub(super) metric_quantization_decimal_places: u8,
157-
pub(super) optimizer: ActiveOptimizerSnapshot,
158-
}
132+
/// Легковесный отпечаток состояния правой панели для детекта изменений без аллокаций.
133+
type RightPanelFitFingerprint = u64;
159134

160135
#[derive(Debug)]
161136
/// Трасса одной итерации параметрического фитинга для replay/диагностики.
@@ -279,7 +254,7 @@ pub struct CurveFitApp {
279254
pub(super) spline_initial_knot_y_inputs: Vec<String>,
280255
pub(super) auto_refit_enabled: bool,
281256
pub(super) auto_refit_pending_rerun: bool,
282-
pub(super) last_right_panel_fit_snapshot: Option<RightPanelFitSnapshot>,
257+
pub(super) last_right_panel_fit_snapshot: Option<RightPanelFitFingerprint>,
283258
pub(super) fit_in_progress: bool,
284259
pub(super) fit_loss_metric: OptimizationLossMetric,
285260
pub(super) fit_metric_quantization: MetricQuantization,
@@ -458,39 +433,83 @@ impl CurveFitApp {
458433
self.active_optimizer_view().config()
459434
}
460435

461-
pub(super) fn capture_active_optimizer_snapshot(&self) -> ActiveOptimizerSnapshot {
436+
fn hash_f64<H: Hasher>(hasher: &mut H, value: f64) {
437+
let normalized_bits = if value == 0.0 {
438+
0.0f64.to_bits()
439+
} else {
440+
value.to_bits()
441+
};
442+
normalized_bits.hash(hasher);
443+
}
444+
445+
fn hash_active_optimizer_inputs<H: Hasher>(&self, hasher: &mut H) {
446+
std::mem::discriminant(&self.optimizer_method).hash(hasher);
462447
match self.optimizer_method {
463-
OptimizerMethod::Lbfgs => ActiveOptimizerSnapshot::Lbfgs(self.lbfgs_inputs.clone()),
448+
OptimizerMethod::Lbfgs => {
449+
self.lbfgs_inputs.history_size.hash(hasher);
450+
self.lbfgs_inputs.max_iters.hash(hasher);
451+
Self::hash_f64(hasher, self.lbfgs_inputs.tol_grad);
452+
Self::hash_f64(hasher, self.lbfgs_inputs.tol_cost);
453+
Self::hash_f64(hasher, self.lbfgs_inputs.c1);
454+
Self::hash_f64(hasher, self.lbfgs_inputs.c2);
455+
Self::hash_f64(hasher, self.lbfgs_inputs.step_min);
456+
Self::hash_f64(hasher, self.lbfgs_inputs.step_max);
457+
Self::hash_f64(hasher, self.lbfgs_inputs.width_tolerance);
458+
}
464459
OptimizerMethod::NelderMead => {
465-
ActiveOptimizerSnapshot::NelderMead(self.nelder_mead_inputs.clone())
460+
self.nelder_mead_inputs.max_iters.hash(hasher);
461+
Self::hash_f64(hasher, self.nelder_mead_inputs.simplex_scale);
462+
Self::hash_f64(hasher, self.nelder_mead_inputs.sd_tolerance);
463+
Self::hash_f64(hasher, self.nelder_mead_inputs.alpha);
464+
Self::hash_f64(hasher, self.nelder_mead_inputs.gamma);
465+
Self::hash_f64(hasher, self.nelder_mead_inputs.rho);
466+
Self::hash_f64(hasher, self.nelder_mead_inputs.sigma);
466467
}
467468
OptimizerMethod::SteepestDescent => {
468-
ActiveOptimizerSnapshot::SteepestDescent(self.steepest_descent_inputs.clone())
469+
self.steepest_descent_inputs.max_iters.hash(hasher);
470+
Self::hash_f64(hasher, self.steepest_descent_inputs.c1);
471+
Self::hash_f64(hasher, self.steepest_descent_inputs.c2);
472+
Self::hash_f64(hasher, self.steepest_descent_inputs.step_min);
473+
Self::hash_f64(hasher, self.steepest_descent_inputs.step_max);
474+
Self::hash_f64(hasher, self.steepest_descent_inputs.width_tolerance);
469475
}
470476
OptimizerMethod::NewtonCg => {
471-
ActiveOptimizerSnapshot::NewtonCg(self.newton_cg_inputs.clone())
477+
self.newton_cg_inputs.max_iters.hash(hasher);
478+
Self::hash_f64(hasher, self.newton_cg_inputs.tol);
479+
Self::hash_f64(hasher, self.newton_cg_inputs.curvature_threshold);
480+
Self::hash_f64(hasher, self.newton_cg_inputs.c1);
481+
Self::hash_f64(hasher, self.newton_cg_inputs.c2);
482+
Self::hash_f64(hasher, self.newton_cg_inputs.step_min);
483+
Self::hash_f64(hasher, self.newton_cg_inputs.step_max);
484+
Self::hash_f64(hasher, self.newton_cg_inputs.width_tolerance);
485+
}
486+
OptimizerMethod::Sgd => {
487+
self.sgd_inputs.max_iters.hash(hasher);
488+
Self::hash_f64(hasher, self.sgd_inputs.learning_rate);
489+
}
490+
OptimizerMethod::Adam => {
491+
self.adam_inputs.max_iters.hash(hasher);
492+
Self::hash_f64(hasher, self.adam_inputs.learning_rate);
472493
}
473-
OptimizerMethod::Sgd => ActiveOptimizerSnapshot::Sgd(self.sgd_inputs.clone()),
474-
OptimizerMethod::Adam => ActiveOptimizerSnapshot::Adam(self.adam_inputs.clone()),
475494
}
476495
}
477496

478-
pub(super) fn capture_right_panel_fit_snapshot(&self) -> RightPanelFitSnapshot {
479-
RightPanelFitSnapshot {
480-
selected_model: self.selected_model,
481-
polynomial_degree: self.polynomial_degree,
482-
rational_degree: self.rational_degree,
483-
parameter_inputs: self.parameter_inputs.clone(),
484-
spline_knots: self.spline_knots,
485-
spline_knot_strategy: self.spline_knot_strategy,
486-
spline_extrapolation: self.spline_extrapolation,
487-
spline_duplicate_x_policy: self.spline_duplicate_x_policy,
488-
spline_initial_knot_y_inputs: self.spline_initial_knot_y_inputs.clone(),
489-
optimization_loss_metric: self.optimization_loss_metric,
490-
metric_quantization_enabled: self.metric_quantization_enabled,
491-
metric_quantization_decimal_places: self.metric_quantization_decimal_places,
492-
optimizer: self.capture_active_optimizer_snapshot(),
493-
}
497+
pub(super) fn capture_right_panel_fit_snapshot(&self) -> RightPanelFitFingerprint {
498+
let mut hasher = DefaultHasher::new();
499+
std::mem::discriminant(&self.selected_model).hash(&mut hasher);
500+
self.polynomial_degree.hash(&mut hasher);
501+
self.rational_degree.hash(&mut hasher);
502+
self.parameter_inputs.hash(&mut hasher);
503+
self.spline_knots.hash(&mut hasher);
504+
std::mem::discriminant(&self.spline_knot_strategy).hash(&mut hasher);
505+
std::mem::discriminant(&self.spline_extrapolation).hash(&mut hasher);
506+
std::mem::discriminant(&self.spline_duplicate_x_policy).hash(&mut hasher);
507+
self.spline_initial_knot_y_inputs.hash(&mut hasher);
508+
std::mem::discriminant(&self.optimization_loss_metric).hash(&mut hasher);
509+
self.metric_quantization_enabled.hash(&mut hasher);
510+
self.metric_quantization_decimal_places.hash(&mut hasher);
511+
self.hash_active_optimizer_inputs(&mut hasher);
512+
hasher.finish()
494513
}
495514

496515
pub(super) fn track_right_panel_fit_changes_and_maybe_refit(&mut self) {

src/domain/point.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Типы точки и набора точек с инвариантами на конечность координат и минимальный размер.
22
33
use super::InputError;
4+
use std::sync::Arc;
5+
46
const MIN_POINTS: usize = 2;
57

68
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -42,7 +44,7 @@ impl Point {
4244
#[derive(Debug, Clone, PartialEq)]
4345
/// Набор точек с инвариантом минимального размера (`>= 2`).
4446
pub struct Points {
45-
points: Box<[Point]>,
47+
points: Arc<[Point]>,
4648
}
4749

4850
impl Points {
@@ -88,7 +90,7 @@ impl TryFrom<Vec<Point>> for Points {
8890
});
8991
}
9092
Ok(Self {
91-
points: points.into_boxed_slice(),
93+
points: Arc::from(points),
9294
})
9395
}
9496
}

src/fit/finite_diff.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ pub(super) fn array1_as_slice(values: &Array1<f64>) -> &[f64] {
3838
.expect("Array1 parameters must have contiguous memory layout")
3939
}
4040

41+
pub(super) fn array1_as_mut_slice(values: &mut Array1<f64>) -> &mut [f64] {
42+
values
43+
.as_slice_mut()
44+
.expect("Array1 parameters must have contiguous memory layout")
45+
}
46+
4147
pub(super) fn stabilize_hessian(hessian: &mut Array2<f64>) {
4248
let dimension = hessian.nrows();
4349
debug_assert_eq!(dimension, hessian.ncols());

0 commit comments

Comments
 (0)