|
1 | 1 | //! Центральные типы состояния приложения, снимки UI и сообщения рантайма фитинга. |
2 | 2 |
|
3 | 3 | use super::*; |
| 4 | +use std::hash::{DefaultHasher, Hash, Hasher}; |
4 | 5 |
|
5 | 6 | /// Неизменяемое представление выбранного оптимизатора и его input-состояния. |
6 | 7 | pub(super) enum ActiveOptimizerView<'a> { |
@@ -128,34 +129,8 @@ impl ActiveOptimizerViewMut<'_> { |
128 | 129 | } |
129 | 130 | } |
130 | 131 |
|
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; |
159 | 134 |
|
160 | 135 | #[derive(Debug)] |
161 | 136 | /// Трасса одной итерации параметрического фитинга для replay/диагностики. |
@@ -279,7 +254,7 @@ pub struct CurveFitApp { |
279 | 254 | pub(super) spline_initial_knot_y_inputs: Vec<String>, |
280 | 255 | pub(super) auto_refit_enabled: bool, |
281 | 256 | 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>, |
283 | 258 | pub(super) fit_in_progress: bool, |
284 | 259 | pub(super) fit_loss_metric: OptimizationLossMetric, |
285 | 260 | pub(super) fit_metric_quantization: MetricQuantization, |
@@ -458,39 +433,83 @@ impl CurveFitApp { |
458 | 433 | self.active_optimizer_view().config() |
459 | 434 | } |
460 | 435 |
|
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); |
462 | 447 | 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 | + } |
464 | 459 | 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); |
466 | 467 | } |
467 | 468 | 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); |
469 | 475 | } |
470 | 476 | 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); |
472 | 493 | } |
473 | | - OptimizerMethod::Sgd => ActiveOptimizerSnapshot::Sgd(self.sgd_inputs.clone()), |
474 | | - OptimizerMethod::Adam => ActiveOptimizerSnapshot::Adam(self.adam_inputs.clone()), |
475 | 494 | } |
476 | 495 | } |
477 | 496 |
|
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() |
494 | 513 | } |
495 | 514 |
|
496 | 515 | pub(super) fn track_right_panel_fit_changes_and_maybe_refit(&mut self) { |
|
0 commit comments