Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added (p *VCurveParams) LevenbergMarquardtOptimisation() to vcurve module in IRIS. #215

Merged
merged 1 commit into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
Loading