@@ -671,3 +671,134 @@ func TestFritschButlandErrors(t *testing.T) {
671671 }
672672 }
673673}
674+
675+ func TestPiecewiseCubicFitWithSecondDerivatives (t * testing.T ) {
676+ t .Parallel ()
677+ const tol = 1e-14
678+ xs := []float64 {- 2 , 0 , 3 }
679+ ys := []float64 {2.5 , 1 , 2.5 }
680+ d2ydx2s := []float64 {1 , 2 , 3 }
681+ var pc PiecewiseCubic
682+ pc .fitWithSecondDerivatives (xs , ys , d2ydx2s )
683+ m := len (xs ) - 1
684+ if pc .lastY != ys [m ] {
685+ t .Errorf ("Mismatch in lastY: got %v, want %g" , pc .lastY , ys [m ])
686+ }
687+ if ! floats .Equal (pc .xs , xs ) {
688+ t .Errorf ("Mismatch in xs: got %v, want %v" , pc .xs , xs )
689+ }
690+ for i := 0 ; i < len (xs ); i ++ {
691+ yHat := pc .Predict (xs [i ])
692+ if math .Abs (yHat - ys [i ]) > tol {
693+ t .Errorf ("Mismatch in predicted Y[%d]: got %v, want %g" , i , yHat , ys [i ])
694+ }
695+ var d2ydx2Hat float64
696+ if i < m {
697+ d2ydx2Hat = 2 * pc .coeffs .At (i , 2 )
698+ } else {
699+ d2ydx2Hat = 2 * pc .coeffs .At (m - 1 , 2 ) + 6 * pc .coeffs .At (m - 1 , 3 )* (xs [m ]- xs [m - 1 ])
700+ }
701+ if math .Abs (d2ydx2Hat - d2ydx2s [i ]) > tol {
702+ t .Errorf ("Mismatch in predicted d2Y/dX2[%d]: got %v, want %g" , i , d2ydx2Hat , d2ydx2s [i ])
703+ }
704+ }
705+ // Test pc.lastDyDx without copying verbatim the calculation from the tested method:
706+ lastDyDx := pc .PredictDerivative (xs [m ] - tol / 1000 )
707+ if math .Abs (lastDyDx - pc .lastDyDx ) > tol {
708+ t .Errorf ("Mismatch in lastDxDy: got %v, want %g" , pc .lastDyDx , lastDyDx )
709+ }
710+ }
711+
712+ func TestPiecewiseCubicFitWithSecondDerivativesErrors (t * testing.T ) {
713+ t .Parallel ()
714+ for _ , test := range []struct {
715+ xs , ys , d2ydx2s []float64
716+ }{
717+ {
718+ xs : []float64 {0 , 1 , 2 },
719+ ys : []float64 {10 , 20 },
720+ d2ydx2s : []float64 {0 , 0 , 0 },
721+ },
722+ {
723+ xs : []float64 {0 , 1 , 1 },
724+ ys : []float64 {10 , 20 , 30 },
725+ d2ydx2s : []float64 {0 , 0 , 0 , 0 },
726+ },
727+ {
728+ xs : []float64 {0 },
729+ ys : []float64 {0 },
730+ d2ydx2s : []float64 {0 },
731+ },
732+ {
733+ xs : []float64 {0 , 1 , 1 },
734+ ys : []float64 {10 , 20 , 10 },
735+ d2ydx2s : []float64 {0 , 0 , 0 },
736+ },
737+ } {
738+ var pc PiecewiseCubic
739+ if ! panics (func () { pc .fitWithSecondDerivatives (test .xs , test .ys , test .d2ydx2s ) }) {
740+ t .Errorf ("expected panic for xs: %v, ys: %v and d2ydx2s: %v" , test .xs , test .ys , test .d2ydx2s )
741+ }
742+ }
743+ }
744+
745+ func TestMakeCubicSplineSecondDerivativeEquations (t * testing.T ) {
746+ t .Parallel ()
747+ const tol = 1e-15
748+ xs := []float64 {- 1 , 0 , 2 }
749+ ys := []float64 {2 , 0 , 2 }
750+ n := len (xs )
751+ A , b := makeCubicSplineSecondDerivativeEquations (xs , ys )
752+ if b .Len () != n {
753+ t .Errorf ("Mismatch in b size: got %v, want %d" , b .Len (), n )
754+ }
755+ r , c := A .Dims ()
756+ if r != n || c != n {
757+ t .Errorf ("Mismatch in A size: got %d x %d, want %d x %d" , r , c , n , n )
758+ }
759+ expectedB := mat .NewVecDense (3 , []float64 {0 , 3 , 0 })
760+ var diffB mat.VecDense
761+ diffB .SubVec (b , expectedB )
762+ if diffB .Norm (math .Inf (1 )) > tol {
763+ t .Errorf ("Mismatch in b values: got %v, want %v" , b , expectedB )
764+ }
765+ expectedA := mat .NewDense (3 , 3 , []float64 {0 , 0 , 0 , 1 / 6. , 1 , 2 / 6. , 0 , 0 , 0 })
766+ var diffA mat.Dense
767+ diffA .Sub (A , expectedA )
768+ if diffA .Norm (math .Inf (1 )) > tol {
769+ t .Errorf ("Mismatch in A values: got %v, want %v" , A , expectedA )
770+ }
771+ }
772+
773+ func TestNaturalCubicFit (t * testing.T ) {
774+ t .Parallel ()
775+ xs := []float64 {- 1 , 0 , 2 , 3.5 }
776+ ys := []float64 {2 , 0 , 2 , 1.5 }
777+ var nc NaturalCubic
778+ err := nc .Fit (xs , ys )
779+ if err != nil {
780+ t .Errorf ("Error when fitting NaturalCubic: %v" , err )
781+ }
782+ testXs := []float64 {- 1 , - 0.99 , - 0.5 , 0 , 0.5 , 1 , 1.5 , 2 , 2.5 , 3 , 3.49 , 3.5 }
783+ // From scipy.interpolate.CubicSpline:
784+ want := []float64 {
785+ 2.0 ,
786+ 1.9737725526315788 ,
787+ 0.7664473684210527 ,
788+ 0.0 ,
789+ 0.027960526315789477 ,
790+ 0.6184210526315789 ,
791+ 1.3996710526315788 ,
792+ 2.0 ,
793+ 2.1403508771929824 ,
794+ 1.9122807017543857 ,
795+ 1.508859403508772 ,
796+ 1.5 }
797+ for i := 0 ; i < len (testXs ); i ++ {
798+ got := nc .Predict (testXs [i ])
799+ if math .Abs (got - want [i ]) > 1e-14 {
800+ t .Errorf ("Mismatch in predicted Y value for x = %g: got %v, want %g" , testXs [i ], got , want [i ])
801+ }
802+ }
803+
804+ }
0 commit comments