@@ -13,10 +13,11 @@ use argmin::solver::quasinewton::LBFGS;
1313
1414use crate :: domain:: {
1515 CurveFamily , CurveParams , FitResult , InputError , LbfgsConfig , NelderMeadConfig ,
16- OptimizerConfig , Point , Points , SteepestDescentConfig ,
16+ OptimizerConfig , Points , SteepestDescentConfig ,
1717} ;
1818
1919mod curve;
20+ mod simd;
2021mod spline;
2122
2223#[ cfg( not( target_arch = "wasm32" ) ) ]
@@ -34,6 +35,94 @@ pub use spline::{
3435 fit_natural_cubic_spline_with_config, fit_natural_cubic_spline_with_optimizer_config,
3536} ;
3637
38+ #[ cfg( feature = "portable-simd" ) ]
39+ #[ doc( hidden) ]
40+ pub mod simd_bench {
41+ use super :: { OptimizationLossMetric , simd} ;
42+
43+ pub fn polynomial_cost_scalar (
44+ param : & [ f64 ] ,
45+ x_values : & [ f64 ] ,
46+ y_values : & [ f64 ] ,
47+ loss_metric : OptimizationLossMetric ,
48+ ) -> f64 {
49+ simd:: polynomial_cost_scalar ( param, x_values, y_values, loss_metric)
50+ }
51+
52+ pub fn polynomial_cost_simd (
53+ param : & [ f64 ] ,
54+ x_values : & [ f64 ] ,
55+ y_values : & [ f64 ] ,
56+ loss_metric : OptimizationLossMetric ,
57+ ) -> f64 {
58+ simd:: polynomial_cost_simd ( param, x_values, y_values, loss_metric)
59+ }
60+
61+ pub fn inverse_cost_scalar (
62+ param : & [ f64 ] ,
63+ x_values : & [ f64 ] ,
64+ y_values : & [ f64 ] ,
65+ loss_metric : OptimizationLossMetric ,
66+ ) -> f64 {
67+ simd:: inverse_cost_scalar ( param, x_values, y_values, loss_metric)
68+ }
69+
70+ pub fn inverse_cost_simd (
71+ param : & [ f64 ] ,
72+ x_values : & [ f64 ] ,
73+ y_values : & [ f64 ] ,
74+ loss_metric : OptimizationLossMetric ,
75+ ) -> f64 {
76+ simd:: inverse_cost_simd ( param, x_values, y_values, loss_metric)
77+ }
78+
79+ pub fn polynomial_gradient_scalar (
80+ x_values : & [ f64 ] ,
81+ y_values : & [ f64 ] ,
82+ param : & [ f64 ] ,
83+ loss_metric : OptimizationLossMetric ,
84+ gradient : & mut [ f64 ] ,
85+ ) {
86+ simd:: accumulate_polynomial_gradient_scalar (
87+ x_values,
88+ y_values,
89+ param,
90+ loss_metric,
91+ gradient,
92+ ) ;
93+ }
94+
95+ pub fn polynomial_gradient_simd (
96+ x_values : & [ f64 ] ,
97+ y_values : & [ f64 ] ,
98+ param : & [ f64 ] ,
99+ loss_metric : OptimizationLossMetric ,
100+ gradient : & mut [ f64 ] ,
101+ ) {
102+ simd:: accumulate_polynomial_gradient_simd ( x_values, y_values, param, loss_metric, gradient) ;
103+ }
104+
105+ pub fn inverse_gradient_scalar (
106+ x_values : & [ f64 ] ,
107+ y_values : & [ f64 ] ,
108+ param : & [ f64 ] ,
109+ loss_metric : OptimizationLossMetric ,
110+ gradient : & mut [ f64 ] ,
111+ ) {
112+ simd:: accumulate_inverse_gradient_scalar ( x_values, y_values, param, loss_metric, gradient) ;
113+ }
114+
115+ pub fn inverse_gradient_simd (
116+ x_values : & [ f64 ] ,
117+ y_values : & [ f64 ] ,
118+ param : & [ f64 ] ,
119+ loss_metric : OptimizationLossMetric ,
120+ gradient : & mut [ f64 ] ,
121+ ) {
122+ simd:: accumulate_inverse_gradient_simd ( x_values, y_values, param, loss_metric, gradient) ;
123+ }
124+ }
125+
37126const PARAM_EPS : f64 = 1e-9 ;
38127const LARGE_COST : f64 = 1e24 ;
39128const LN_2 : f64 = std:: f64:: consts:: LN_2 ;
@@ -174,42 +263,29 @@ pub struct IterationMetricSnapshot {
174263struct CurveProblem {
175264 family : CurveFamily ,
176265 points : Points ,
266+ point_x : Box < [ f64 ] > ,
267+ point_y : Box < [ f64 ] > ,
177268 loss_metric : OptimizationLossMetric ,
178269}
179270
180271impl CurveProblem {
181272 fn new ( family : CurveFamily , points : & Points , loss_metric : OptimizationLossMetric ) -> Self {
273+ let mut point_x = Vec :: with_capacity ( points. len ( ) ) ;
274+ let mut point_y = Vec :: with_capacity ( points. len ( ) ) ;
275+ for point in points. as_slice ( ) {
276+ point_x. push ( point. x ( ) ) ;
277+ point_y. push ( point. y ( ) ) ;
278+ }
182279 Self {
183280 family,
184281 points : points. clone ( ) ,
282+ point_x : point_x. into_boxed_slice ( ) ,
283+ point_y : point_y. into_boxed_slice ( ) ,
185284 loss_metric,
186285 }
187286 }
188287}
189288
190- fn accumulate_polynomial_gradient (
191- points : & [ Point ] ,
192- param : & [ f64 ] ,
193- loss_metric : OptimizationLossMetric ,
194- gradient : & mut [ f64 ] ,
195- ) {
196- debug_assert_eq ! ( gradient. len( ) , param. len( ) ) ;
197- for point in points {
198- let x = point. x ( ) ;
199- let model = param
200- . iter ( )
201- . copied ( )
202- . fold ( 0.0 , |acc, coefficient| acc * x + coefficient) ;
203- let residual = loss_metric. residual_derivative ( model - point. y ( ) ) ;
204-
205- let mut basis = 1.0 ;
206- for gradient_value in gradient. iter_mut ( ) . rev ( ) {
207- * gradient_value += residual * basis;
208- basis *= x;
209- }
210- }
211- }
212-
213289#[ derive( Debug , Clone , PartialEq ) ]
214290/// Подробный результат подгонки сплайна.
215291pub struct SplineResult {
@@ -259,6 +335,21 @@ fn soft_l1_from_residuals(residuals: &[[f64; 2]]) -> f64 {
259335 / residuals. len ( ) as f64
260336}
261337
338+ fn is_polynomial_family ( family : CurveFamily ) -> bool {
339+ matches ! (
340+ family,
341+ CurveFamily :: Linear
342+ | CurveFamily :: Quadratic
343+ | CurveFamily :: Cubic
344+ | CurveFamily :: Quartic
345+ | CurveFamily :: Quintic
346+ | CurveFamily :: Sextic
347+ | CurveFamily :: Septic
348+ | CurveFamily :: Octic
349+ | CurveFamily :: Nonic
350+ )
351+ }
352+
262353/// Число узлов сплайна по умолчанию.
263354pub const DEFAULT_SPLINE_KNOTS : usize = 8 ;
264355/// Число сэмплов кривой для визуализации по умолчанию.
@@ -1069,6 +1160,24 @@ impl CostFunction for CurveProblem {
10691160 type Output = f64 ;
10701161
10711162 fn cost ( & self , param : & Self :: Param ) -> Result < Self :: Output , argmin:: core:: Error > {
1163+ if is_polynomial_family ( self . family ) {
1164+ return Ok ( simd:: polynomial_cost (
1165+ param,
1166+ self . point_x . as_ref ( ) ,
1167+ self . point_y . as_ref ( ) ,
1168+ self . loss_metric ,
1169+ ) ) ;
1170+ }
1171+
1172+ if self . family == CurveFamily :: Inverse {
1173+ return Ok ( simd:: inverse_cost (
1174+ param,
1175+ self . point_x . as_ref ( ) ,
1176+ self . point_y . as_ref ( ) ,
1177+ self . loss_metric ,
1178+ ) ) ;
1179+ }
1180+
10721181 let sample_count = self . points . len ( ) as f64 ;
10731182 let mut sum = 0.0 ;
10741183
@@ -1106,9 +1215,13 @@ impl Gradient for CurveProblem {
11061215 | CurveFamily :: Sextic
11071216 | CurveFamily :: Septic
11081217 | CurveFamily :: Octic
1109- | CurveFamily :: Nonic => {
1110- accumulate_polynomial_gradient ( points, param, self . loss_metric , & mut gradient) ;
1111- }
1218+ | CurveFamily :: Nonic => simd:: accumulate_polynomial_gradient (
1219+ self . point_x . as_ref ( ) ,
1220+ self . point_y . as_ref ( ) ,
1221+ param,
1222+ self . loss_metric ,
1223+ & mut gradient,
1224+ ) ,
11121225 CurveFamily :: Arrhenius => {
11131226 for point in points {
11141227 let x = positive_x ( point. x ( ) ) ;
@@ -1119,16 +1232,13 @@ impl Gradient for CurveProblem {
11191232 gradient[ 1 ] += residual * ( param[ 0 ] * exp_term / x) ;
11201233 }
11211234 }
1122- CurveFamily :: Inverse => {
1123- for point in points {
1124- let x = positive_x ( point. x ( ) ) ;
1125- let residual = self
1126- . loss_metric
1127- . residual_derivative ( param[ 0 ] + param[ 1 ] / x - point. y ( ) ) ;
1128- gradient[ 0 ] += residual;
1129- gradient[ 1 ] += residual / x;
1130- }
1131- }
1235+ CurveFamily :: Inverse => simd:: accumulate_inverse_gradient (
1236+ self . point_x . as_ref ( ) ,
1237+ self . point_y . as_ref ( ) ,
1238+ param,
1239+ self . loss_metric ,
1240+ & mut gradient,
1241+ ) ,
11321242 CurveFamily :: Logistic => {
11331243 for point in points {
11341244 let z = param[ 1 ] * ( point. x ( ) - param[ 2 ] ) ;
0 commit comments