11use super :: common:: { is_finite_non_negative, scale_and_mirror_upper_hessian} ;
22use ndarray:: Array2 ;
33
4+ const PARAM_COUNT : usize = 4 ;
5+
6+ #[ derive( Clone , Copy ) ]
7+ struct Params < T > {
8+ amplitude : T ,
9+ slope : T ,
10+ x0 : T ,
11+ offset : T ,
12+ }
13+
14+ impl Params < f64 > {
15+ #[ inline]
16+ fn parse ( param : & [ f64 ] ) -> Self {
17+ let [ amplitude, slope, x0, offset] : [ f64 ; PARAM_COUNT ] = param
18+ . try_into ( )
19+ . unwrap_or_else ( |_| panic ! ( "expected {} params" , PARAM_COUNT ) ) ;
20+ Self {
21+ amplitude,
22+ slope,
23+ x0,
24+ offset,
25+ }
26+ }
27+
28+ #[ inline]
29+ fn value_at ( self , x : f64 ) -> f64 {
30+ self . amplitude * ( self . slope * ( x - self . x0 ) ) . atan ( ) + self . offset
31+ }
32+
33+ #[ inline]
34+ fn value_grad_at ( self , x : f64 , grad : & mut [ f64 ] ) -> f64 {
35+ debug_assert_eq ! ( grad. len( ) , PARAM_COUNT ) ;
36+
37+ let z = self . slope * ( x - self . x0 ) ;
38+ let atan_z = z. atan ( ) ;
39+ let inv_den = 1.0 / ( 1.0 + z * z) ;
40+
41+ grad[ 0 ] = atan_z;
42+ grad[ 1 ] = self . amplitude * ( x - self . x0 ) * inv_den;
43+ grad[ 2 ] = -self . amplitude * self . slope * inv_den;
44+ grad[ 3 ] = 1.0 ;
45+
46+ self . amplitude * atan_z + self . offset
47+ }
48+ }
49+
450/// Вычисляет арктангенс-ступень:
551/// `f(x) = amplitude * atan(slope * (x - x0)) + offset`,
652/// где:
@@ -10,31 +56,13 @@ use ndarray::Array2;
1056/// - `offset` — вертикальный сдвиг.
1157#[ inline]
1258pub ( super ) fn value_at ( param : & [ f64 ] , x : f64 ) -> f64 {
13- let amplitude = param[ 0 ] ;
14- let slope = param[ 1 ] ;
15- let x0 = param[ 2 ] ;
16- let offset = param[ 3 ] ;
17- amplitude * ( slope * ( x - x0) ) . atan ( ) + offset
59+ Params :: parse ( param) . value_at ( x)
1860}
1961
62+ #[ allow( dead_code) ]
2063#[ inline]
2164pub ( super ) fn value_grad_at ( param : & [ f64 ] , x : f64 , grad : & mut [ f64 ] ) -> f64 {
22- debug_assert_eq ! ( grad. len( ) , 4 ) ;
23-
24- let amplitude = param[ 0 ] ;
25- let slope = param[ 1 ] ;
26- let x0 = param[ 2 ] ;
27- let offset = param[ 3 ] ;
28- let z = slope * ( x - x0) ;
29- let atan_z = z. atan ( ) ;
30- let inv_den = 1.0 / ( 1.0 + z * z) ;
31-
32- grad[ 0 ] = atan_z;
33- grad[ 1 ] = amplitude * ( x - x0) * inv_den;
34- grad[ 2 ] = -amplitude * slope * inv_den;
35- grad[ 3 ] = 1.0 ;
36-
37- amplitude * atan_z + offset
65+ Params :: parse ( param) . value_grad_at ( x, grad)
3866}
3967
4068pub ( super ) fn add_value_grad (
@@ -45,18 +73,15 @@ pub(super) fn add_value_grad(
4573) {
4674 debug_assert_eq ! ( x_values. len( ) , value_first. len( ) ) ;
4775 debug_assert_eq ! ( gradient. len( ) , param. len( ) ) ;
76+ let params = Params :: parse ( param) ;
4877
49- let mut point_grad = [ 0.0 ; 4 ] ;
50- let mut index = 0 ;
51- while index < x_values. len ( ) {
52- let upstream = value_first[ index] ;
53- value_grad_at ( param, x_values[ index] , & mut point_grad) ;
54-
55- gradient[ 0 ] += upstream * point_grad[ 0 ] ;
56- gradient[ 1 ] += upstream * point_grad[ 1 ] ;
57- gradient[ 2 ] += upstream * point_grad[ 2 ] ;
58- gradient[ 3 ] += upstream * point_grad[ 3 ] ;
59- index += 1 ;
78+ let mut point_grad = [ 0.0 ; PARAM_COUNT ] ;
79+ for ( & x, & upstream) in x_values. iter ( ) . zip ( value_first. iter ( ) ) {
80+ params. value_grad_at ( x, & mut point_grad) ;
81+
82+ for ( gradient_value, point_grad_value) in gradient. iter_mut ( ) . zip ( point_grad. iter ( ) ) {
83+ * gradient_value += upstream * point_grad_value;
84+ }
6085 }
6186}
6287
@@ -66,26 +91,24 @@ pub(super) fn add_value_grad_raw_hessian(
6691 value_first : & [ f64 ] ,
6792 value_second : & [ f64 ] ,
6893) -> Option < Array2 < f64 > > {
69- if param. len ( ) != 4 {
94+ if param. len ( ) != PARAM_COUNT {
7095 return None ;
7196 }
7297
7398 let sample_count = x_values. len ( ) ;
7499 let sample_scale = 1.0 / sample_count as f64 ;
75- let mut hessian = Array2 :: zeros ( ( 4 , 4 ) ) ;
76- let amplitude = param[ 0 ] ;
77- let slope = param[ 1 ] ;
78- let x0 = param[ 2 ] ;
100+ let mut hessian = Array2 :: zeros ( ( PARAM_COUNT , PARAM_COUNT ) ) ;
101+ let params = Params :: parse ( param) ;
79102
80103 let mut index = 0 ;
81104 while index < sample_count {
82105 let x = x_values[ index] ;
83- let u = x - x0;
84- let z = slope * u;
106+ let u = x - params . x0 ;
107+ let z = params . slope * u;
85108 let atan_z = z. atan ( ) ;
86109 let inv_den = 1.0 / ( 1.0 + z * z) ;
87110 let d2_shape_dz2 = -2.0 * z * inv_den * inv_den;
88- let model = value_at ( param , x) ;
111+ let model = params . value_at ( x) ;
89112 if !model. is_finite ( ) {
90113 return None ;
91114 }
@@ -97,15 +120,15 @@ pub(super) fn add_value_grad_raw_hessian(
97120 }
98121
99122 let jac_a = atan_z;
100- let jac_b = amplitude * inv_den * u;
101- let jac_c = -amplitude * inv_den * slope;
123+ let jac_b = params . amplitude * inv_den * u;
124+ let jac_c = -params . amplitude * inv_den * params . slope ;
102125 let jac_d = 1.0 ;
103126
104127 let d2_model_dadb = inv_den * u;
105- let d2_model_dadc = -inv_den * slope;
106- let d2_model_dbdb = amplitude * d2_shape_dz2 * u * u;
107- let d2_model_dbdc = amplitude * ( d2_shape_dz2 * ( -slope) * u - inv_den) ;
108- let d2_model_dcdc = amplitude * d2_shape_dz2 * slope * slope;
128+ let d2_model_dadc = -inv_den * params . slope ;
129+ let d2_model_dbdb = params . amplitude * d2_shape_dz2 * u * u;
130+ let d2_model_dbdc = params . amplitude * ( d2_shape_dz2 * ( -params . slope ) * u - inv_den) ;
131+ let d2_model_dcdc = params . amplitude * d2_shape_dz2 * params . slope * params . slope ;
109132
110133 hessian[ [ 0 , 0 ] ] += value_second * jac_a * jac_a;
111134 hessian[ [ 0 , 1 ] ] += value_second * jac_a * jac_b + value_first * d2_model_dadb;
0 commit comments