Skip to content

Commit

Permalink
Merge pull request #215 from observerly/feature/vcurve/LevenbergMarqu…
Browse files Browse the repository at this point in the history
…ardtOptimisation

feat: Added (p *VCurveParams) LevenbergMarquardtOptimisation() to vcurve module in IRIS.
  • Loading branch information
michealroberts committed Dec 17, 2023
2 parents bf22f31 + 33ddec5 commit 01e217d
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 28 deletions.
71 changes: 43 additions & 28 deletions coverage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ github.com/observerly/iris/pkg/utils/utils.go:72.44,74.30 2 0
github.com/observerly/iris/pkg/utils/utils.go:74.30,75.31 1 0
github.com/observerly/iris/pkg/utils/utils.go:75.31,77.7 1 0
github.com/observerly/iris/pkg/utils/utils.go:81.4,81.20 1 0
github.com/observerly/iris/pkg/vcurve/vcurve.go:39.53,45.36 3 2
github.com/observerly/iris/pkg/vcurve/vcurve.go:45.36,48.3 2 42
github.com/observerly/iris/pkg/vcurve/vcurve.go:51.2,66.3 4 2
github.com/observerly/iris/pkg/vcurve/vcurve.go:70.65,73.2 2 23688
github.com/observerly/iris/pkg/vcurve/vcurve.go:76.75,78.30 1 1
github.com/observerly/iris/pkg/vcurve/vcurve.go:78.30,79.55 1 0
github.com/observerly/iris/pkg/vcurve/vcurve.go:83.2,83.20 1 1
github.com/observerly/iris/pkg/vcurve/vcurve.go:83.20,84.57 1 0
github.com/observerly/iris/pkg/vcurve/vcurve.go:88.2,88.40 1 1
github.com/observerly/iris/pkg/vcurve/vcurve.go:88.40,90.24 2 1128
github.com/observerly/iris/pkg/vcurve/vcurve.go:90.24,98.4 2 23688
github.com/observerly/iris/pkg/vcurve/vcurve.go:99.3,99.15 1 1128
github.com/observerly/iris/pkg/vcurve/vcurve.go:108.79,126.16 5 1
github.com/observerly/iris/pkg/vcurve/vcurve.go:126.16,128.3 1 0
github.com/observerly/iris/pkg/vcurve/vcurve.go:131.2,136.8 1 1
github.com/observerly/iris/pkg/histogram/histogram.go:20.52,27.47 4 1
github.com/observerly/iris/pkg/histogram/histogram.go:27.47,30.3 2 1708784
github.com/observerly/iris/pkg/histogram/histogram.go:32.2,32.12 1 1
Expand Down Expand Up @@ -222,15 +237,15 @@ github.com/observerly/iris/pkg/statistics/stats.go:193.24,198.3 3 770008
github.com/observerly/iris/pkg/statistics/stats.go:202.2,204.11 2 5
github.com/observerly/iris/pkg/statistics/stats.go:218.100,224.24 3 3
github.com/observerly/iris/pkg/statistics/stats.go:224.24,226.7 2 769000
github.com/observerly/iris/pkg/statistics/stats.go:226.7,229.43 2 769114
github.com/observerly/iris/pkg/statistics/stats.go:226.7,229.43 2 769132
github.com/observerly/iris/pkg/statistics/stats.go:229.43,230.10 1 769000
github.com/observerly/iris/pkg/statistics/stats.go:234.3,234.16 1 769000
github.com/observerly/iris/pkg/statistics/stats.go:237.2,239.15 2 3
github.com/observerly/iris/pkg/statistics/stats.go:253.96,259.24 3 3
github.com/observerly/iris/pkg/statistics/stats.go:259.24,262.7 2 769000
github.com/observerly/iris/pkg/statistics/stats.go:262.7,266.43 3 769175
github.com/observerly/iris/pkg/statistics/stats.go:266.43,267.13 1 119
github.com/observerly/iris/pkg/statistics/stats.go:270.4,271.45 2 769056
github.com/observerly/iris/pkg/statistics/stats.go:262.7,266.43 3 769178
github.com/observerly/iris/pkg/statistics/stats.go:266.43,267.13 1 117
github.com/observerly/iris/pkg/statistics/stats.go:270.4,271.45 2 769061
github.com/observerly/iris/pkg/statistics/stats.go:271.45,272.10 1 769000
github.com/observerly/iris/pkg/statistics/stats.go:276.3,276.50 1 769000
github.com/observerly/iris/pkg/statistics/stats.go:281.2,283.11 2 3
Expand Down Expand Up @@ -429,7 +444,7 @@ github.com/observerly/iris/pkg/photometry/stars.go:67.101,73.2 3 2
github.com/observerly/iris/pkg/photometry/stars.go:80.97,111.2 13 1
github.com/observerly/iris/pkg/photometry/stars.go:121.119,125.25 2 95855
github.com/observerly/iris/pkg/photometry/stars.go:125.25,129.39 2 862695
github.com/observerly/iris/pkg/photometry/stars.go:129.39,132.4 2 862200
github.com/observerly/iris/pkg/photometry/stars.go:129.39,132.4 2 862227
github.com/observerly/iris/pkg/photometry/stars.go:135.2,137.23 2 95855
github.com/observerly/iris/pkg/photometry/stars.go:148.89,152.25 2 7
github.com/observerly/iris/pkg/photometry/stars.go:152.25,153.20 1 10253216
Expand All @@ -445,35 +460,35 @@ github.com/observerly/iris/pkg/photometry/stars.go:217.29,223.44 3 10420
github.com/observerly/iris/pkg/photometry/stars.go:223.44,226.4 2 10173
github.com/observerly/iris/pkg/photometry/stars.go:229.2,229.31 1 5
github.com/observerly/iris/pkg/photometry/stars.go:232.85,249.26 8 6
github.com/observerly/iris/pkg/photometry/stars.go:249.26,254.38 2 8581
github.com/observerly/iris/pkg/photometry/stars.go:254.38,255.41 1 18281
github.com/observerly/iris/pkg/photometry/stars.go:249.26,254.38 2 8582
github.com/observerly/iris/pkg/photometry/stars.go:254.38,255.41 1 18284
github.com/observerly/iris/pkg/photometry/stars.go:255.41,256.13 1 1130
github.com/observerly/iris/pkg/photometry/stars.go:259.4,259.39 1 17151
github.com/observerly/iris/pkg/photometry/stars.go:259.39,260.42 1 44135
github.com/observerly/iris/pkg/photometry/stars.go:259.4,259.39 1 17154
github.com/observerly/iris/pkg/photometry/stars.go:259.39,260.42 1 44144
github.com/observerly/iris/pkg/photometry/stars.go:260.42,261.14 1 4636
github.com/observerly/iris/pkg/photometry/stars.go:265.5,268.53 2 39499
github.com/observerly/iris/pkg/photometry/stars.go:268.53,275.22 5 224543
github.com/observerly/iris/pkg/photometry/stars.go:265.5,268.53 2 39508
github.com/observerly/iris/pkg/photometry/stars.go:268.53,275.22 5 224935
github.com/observerly/iris/pkg/photometry/stars.go:275.22,276.27 1 7278
github.com/observerly/iris/pkg/photometry/stars.go:283.3,293.17 5 1303
github.com/observerly/iris/pkg/photometry/stars.go:283.3,293.17 5 1304
github.com/observerly/iris/pkg/photometry/stars.go:293.17,295.4 1 180
github.com/observerly/iris/pkg/photometry/stars.go:295.9,296.24 1 1123
github.com/observerly/iris/pkg/photometry/stars.go:296.24,298.5 1 3957
github.com/observerly/iris/pkg/photometry/stars.go:300.4,300.38 1 1123
github.com/observerly/iris/pkg/photometry/stars.go:303.3,303.19 1 1303
github.com/observerly/iris/pkg/photometry/stars.go:295.9,296.24 1 1124
github.com/observerly/iris/pkg/photometry/stars.go:296.24,298.5 1 3966
github.com/observerly/iris/pkg/photometry/stars.go:300.4,300.38 1 1124
github.com/observerly/iris/pkg/photometry/stars.go:303.3,303.19 1 1304
github.com/observerly/iris/pkg/photometry/stars.go:306.2,309.31 3 6
github.com/observerly/iris/pkg/photometry/stars.go:319.106,321.26 1 3
github.com/observerly/iris/pkg/photometry/stars.go:321.26,324.71 2 664
github.com/observerly/iris/pkg/photometry/stars.go:324.71,330.39 3 1283
github.com/observerly/iris/pkg/photometry/stars.go:330.39,331.40 1 42339
github.com/observerly/iris/pkg/photometry/stars.go:331.40,336.46 3 1397187
github.com/observerly/iris/pkg/photometry/stars.go:336.46,338.20 2 1374912
github.com/observerly/iris/pkg/photometry/stars.go:338.20,340.8 1 1360667
github.com/observerly/iris/pkg/photometry/stars.go:343.6,345.19 3 1397187
github.com/observerly/iris/pkg/photometry/stars.go:350.4,353.19 3 1283
github.com/observerly/iris/pkg/photometry/stars.go:321.26,324.71 2 665
github.com/observerly/iris/pkg/photometry/stars.go:324.71,330.39 3 1285
github.com/observerly/iris/pkg/photometry/stars.go:330.39,331.40 1 42405
github.com/observerly/iris/pkg/photometry/stars.go:331.40,336.46 3 1399365
github.com/observerly/iris/pkg/photometry/stars.go:336.46,338.20 2 1377090
github.com/observerly/iris/pkg/photometry/stars.go:338.20,340.8 1 1362841
github.com/observerly/iris/pkg/photometry/stars.go:343.6,345.19 3 1399365
github.com/observerly/iris/pkg/photometry/stars.go:350.4,353.19 3 1285
github.com/observerly/iris/pkg/photometry/stars.go:353.19,355.5 1 0
github.com/observerly/iris/pkg/photometry/stars.go:357.4,372.44 10 1283
github.com/observerly/iris/pkg/photometry/stars.go:372.44,374.5 1 1283
github.com/observerly/iris/pkg/photometry/stars.go:376.4,384.16 2 1283
github.com/observerly/iris/pkg/photometry/stars.go:357.4,372.44 10 1285
github.com/observerly/iris/pkg/photometry/stars.go:372.44,374.5 1 1285
github.com/observerly/iris/pkg/photometry/stars.go:376.4,384.16 2 1285
github.com/observerly/iris/pkg/photometry/stars.go:388.2,388.14 1 3
github.com/observerly/iris/pkg/photometry/stars.go:398.143,403.26 3 2
github.com/observerly/iris/pkg/photometry/stars.go:403.26,411.32 4 417
Expand All @@ -494,7 +509,7 @@ github.com/observerly/iris/pkg/photometry/stars.go:454.43,457.29 2 116129
github.com/observerly/iris/pkg/photometry/stars.go:457.29,458.14 1 44028
github.com/observerly/iris/pkg/photometry/stars.go:461.5,465.47 3 72101
github.com/observerly/iris/pkg/photometry/stars.go:465.47,467.15 2 71847
github.com/observerly/iris/pkg/photometry/stars.go:467.15,469.7 1 54125
github.com/observerly/iris/pkg/photometry/stars.go:467.15,469.7 1 54117
github.com/observerly/iris/pkg/photometry/stars.go:471.5,472.18 2 72101
github.com/observerly/iris/pkg/photometry/stars.go:477.3,481.81 3 417
github.com/observerly/iris/pkg/photometry/stars.go:481.81,482.12 1 79
Expand Down
71 changes: 71 additions & 0 deletions pkg/vcurve/vcurve.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math"

"gonum.org/v1/gonum/floats"
"gonum.org/v1/gonum/optimize"
"gonum.org/v1/gonum/stat"
)

Expand Down Expand Up @@ -64,3 +65,73 @@ func NewHyperbolicVCurve(data VCurve) *VCurveParams {
y: dataY,
}
}

// This is the hyperbolic function that we want to fit to the data.
func hyperbolicFunction(x float64, params VCurveParams) float64 {
a, b, c, d := params.A, params.B, params.C, params.D
return b*math.Sqrt(1+math.Pow((x-c)/a, 2)) + d
}

// objectiveFunc is the least squares objective function that accepts dataX and dataY.
func objectiveFunc(dataX, dataY []float64) func(params []float64) float64 {
// If we do not have the same number of x and y data points, we cannot fit the model.
if len(dataX) != len(dataY) {
panic("x and y data points must be the same length")
}

// If we do not have at least 1 data point, we cannot fit the model.
if len(dataX) < 1 {
panic("x data points must have at least 1 data point")
}

// Return the objective function.
return func(params []float64) float64 {
var sumSq float64
for i := range dataX {
yPredicted := hyperbolicFunction(dataX[i], VCurveParams{
A: params[0],
B: params[1],
C: params[2],
D: params[3],
})
sumSq += math.Pow(dataY[i]-yPredicted, 2)
}
return sumSq
}
}

/*
LevenbergMarquardtOptimisation
LevenbergMarquardtOptimisation optimizes the hyperbolic function using the Levenberg-Marquardt algorithm.
*/
func (p *VCurveParams) LevenbergMarquardtOptimisation() (VCurveParams, error) {
// Setting up the optimizer:
problem := optimize.Problem{
Func: objectiveFunc(p.x, p.y),
}

// Create custom settings for the optimization:
settings := &optimize.Settings{
GradientThreshold: 1e-6, // Customize as needed
// Add other settings as needed
}

// Initial guess for parameters:
initialParams := []float64{p.A, p.B, p.C, p.D}

// Run the optimization:
result, err := optimize.Minimize(problem, initialParams, settings, nil)

if err != nil {
return VCurveParams{}, err
}

// Return Optimized parameters
return VCurveParams{
A: result.X[0],
B: result.X[1],
C: result.X[2],
D: result.X[3],
}, nil
}
38 changes: 38 additions & 0 deletions pkg/vcurve/vcurve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,41 @@ func TestNewHyperbolicVCurve(t *testing.T) {
t.Errorf("D should be 0, but got %v", d)
}
}

// Test for V-curve fitting with hyperbolic model
func TestHyperbolicVCurveLevenbergMarquardtOptimisation(t *testing.T) {
v := NewHyperbolicVCurve(VCurve{
Points: points,
})

optimisedParams, err := v.LevenbergMarquardtOptimisation()

// Expect no error!
if err != nil {
t.Errorf("expected no error, but got %v", err)
}

// Deconstruct the VCurveParams struct
a, b, c, d := optimisedParams.A, optimisedParams.B, optimisedParams.C, optimisedParams.D

// Expect the optimised parameters to be close to the actual parameters:
// a = 106.8247
if a-106.8247 > 0.0001 {
t.Errorf("A should be close to %v, but got %v", 106.8247, a)
}

// b = 4.4771
if b-4.4771 > 0.0001 {
t.Errorf("B should be close to %v, but got %v", 4.4771, b)
}

// c = 30008.5444
if c-30008.5444 > 0.0001 {
t.Errorf("C should be close to %v, but got %v", 30008.5444, c)
}

// d = -1.7965
if d+1.7965 > 0.0001 {
t.Errorf("D should be close to %v, but got %v", -1.7965, d)
}
}

0 comments on commit 01e217d

Please sign in to comment.