From 7193209abf5ea32334382a0c4a12803380a25dd4 Mon Sep 17 00:00:00 2001 From: Littleunidragon Date: Wed, 10 Jan 2024 19:06:47 +0200 Subject: [PATCH 1/4] not sure if right, but it will do all my derivatives homework now --- go.mod | 1 + go.sum | 2 ++ main.go | 41 +++++++++++++++++++++++++++++++++++------ main_test.go | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 main_test.go diff --git a/go.mod b/go.mod index fd11da7..3be8798 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( require ( git.sr.ht/~sbinet/gg v0.5.0 // indirect + github.com/Knetic/govaluate v3.0.0+incompatible // indirect github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect github.com/campoy/embedmd v1.0.0 // indirect github.com/ebitengine/purego v0.5.0 // indirect diff --git a/go.sum b/go.sum index 3acb0f1..90ff35c 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dk git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8= git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= +github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= diff --git a/main.go b/main.go index e39985b..a4df3b6 100644 --- a/main.go +++ b/main.go @@ -1,28 +1,57 @@ package main import ( + "fmt" "image" "log" + "math" "time" "github.com/hajimehoshi/ebiten/v2" ) -func f(x float64) float64 { return x*x + 5*x - 3 } -func df(x float64) float64 { return 2*x + 5 } +func f(x float64) float64 { return math.Cos(x*x + 3*x + 97) } // FUCTION CONTROL + +// TODO: use govaluate to get function as user input +// i can get string from user, parse it to function, but then what? + +func derivative(f func(float64) float64) func(float64) float64 { + return func(x float64) float64 { + dx := 0.00000000005 // dx -> 0 + return (f(x+dx) - f(x)) / dx //literally todays math lesson. + } +} +func GradientDescent(f func(float64) float64, x0, alpha float64) float64 { + x := x0 + var stop bool + for !stop { + x -= alpha * derivative(f)(x) + if derivative(f)(x) == 0 || math.Abs(derivative(f)(x)) < 0.00001 { + stop = true + } + } + return x +} func main() { ebiten.SetWindowSize(640, 480) ebiten.SetWindowTitle("Gradient descent") + fmt.Println(GradientDescent(f, -math.Pi/6, 0.01)) //CONTROL X0 AND ALPHA HERE FOR PRINTLN OUTPUT + img := make(chan *image.RGBA, 1) go func() { - p := Plot(-5, 0, 0.1, f) - x := 0.0 + p := Plot(-math.Pi, math.Pi/3, 0.01, f) //CONTROL RANGE AND STEP HERE + x := -math.Pi / 6 //CONTROL X0 HERE FOR GRAPHICS img <- p(x) - for i := 0; i < 50; i++ { + var stop bool + for !stop { time.Sleep(30 * time.Millisecond) - x -= df(x) * 0.1 + x -= 0.9 * derivative(f)(x) // CONTROL ALPHA HERE FOR GRAPHICS + // if you put alpha = 0.9, you wil get badly animated film about snake :) + if derivative(f)(x) == 0 || math.Abs(derivative(f)(x)) < 0.00001 { + stop = true + } img <- p(x) } }() diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..5427b71 --- /dev/null +++ b/main_test.go @@ -0,0 +1,32 @@ +package main + +import ( + "math" + "testing" +) + +func TestDerivative(t *testing.T) { + for _, tc := range []struct { + name string + f func(float64) float64 + x float64 + want float64 + }{ + {name: "constant", f: func(x float64) float64 { return 5 }, x: 0.0, want: 0.0}, + {name: "linear", f: func(x float64) float64 { return 2*x + 5 }, x: 0.0, want: 2.0}, + {name: "simple square", f: func(x float64) float64 { return x * x }, x: 2.0, want: 4.0}, + {name: "not so simple square", f: func(x float64) float64 { return x*x + 5*x - 3 }, x: 1.0, want: 7.0}, + {name: "hyperbola", f: func(x float64) float64 { return 1 / x }, x: 2.0, want: -0.25}, + //{name: "complex sin", f: func(x float64) float64 { return math.Sin(x*x + 5*x - 3) }, x: 1.0, want: 7.0}, + //{name: "complex cos", f: func(x float64) float64 { return math.Cos(x*x + 5*x - 3) }, x: 1.0, want: -1.0}, + // for last two precision is < 0.1 ha + } { + t.Run("", func(t *testing.T) { + d := derivative(tc.f) + got := d(tc.x) + if math.Abs(got-tc.want) > 0.0001 { + t.Errorf("Expected derivative(%f) to be %f, but got %f", tc.x, tc.want, got) + } + }) + } +} From 7f72a9ff907bcad42511b8c038e904e11be05afd Mon Sep 17 00:00:00 2001 From: Littleunidragon Date: Wed, 10 Jan 2024 19:40:42 +0200 Subject: [PATCH 2/4] i am commited now --- main_test.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/main_test.go b/main_test.go index 5427b71..c60edda 100644 --- a/main_test.go +++ b/main_test.go @@ -17,9 +17,16 @@ func TestDerivative(t *testing.T) { {name: "simple square", f: func(x float64) float64 { return x * x }, x: 2.0, want: 4.0}, {name: "not so simple square", f: func(x float64) float64 { return x*x + 5*x - 3 }, x: 1.0, want: 7.0}, {name: "hyperbola", f: func(x float64) float64 { return 1 / x }, x: 2.0, want: -0.25}, - //{name: "complex sin", f: func(x float64) float64 { return math.Sin(x*x + 5*x - 3) }, x: 1.0, want: 7.0}, - //{name: "complex cos", f: func(x float64) float64 { return math.Cos(x*x + 5*x - 3) }, x: 1.0, want: -1.0}, - // for last two precision is < 0.1 ha + {name: "diff.135 lpp. N18 a)", f: func(x float64) float64 { return 2*x*x - 3*x }, x: 3, want: 9}, + {name: "diff.135 lpp. N18 b)", f: func(x float64) float64 { return x*x - x + 2 }, x: 0, want: -1}, + {name: "diff.135 lpp. N18 c)", f: func(x float64) float64 { return 4 - 5*x - x*x }, x: 1, want: -7}, + {"diff.135 lpp. N18 d)", func(x float64) float64 { return -3 / x }, 3, 0.3333333}, //1/3 + {"diff.135 lpp. N18 e)", func(x float64) float64 { return 4 / x * x }, -2, 0}, + {"diff.135 lpp. N18 f)", func(x float64) float64 { return 4/x + 1 }, -2, -1}, + {"diff.135 lpp. N18 g)", func(x float64) float64 { return 6/2 - x }, -1, -1}, + {"diff.135 lpp. N18 h)", func(x float64) float64 { return 1 / math.Sqrt(x) }, 4, -0.062499}, + {"diff.135 lpp. N18 i)", func(x float64) float64 { return 2 * math.Sqrt(1-x) }, 0, -1}, + {"diff.135 lpp. N18 j)", func(x float64) float64 { return math.Sqrt(1 + 2*x) }, 4, 0.3333333}, } { t.Run("", func(t *testing.T) { d := derivative(tc.f) From 48b0922c093baa630fd0616345c01321a35d0ee9 Mon Sep 17 00:00:00 2001 From: Littleunidragon Date: Wed, 10 Jan 2024 19:50:35 +0200 Subject: [PATCH 3/4] hmmmmm --- main_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/main_test.go b/main_test.go index c60edda..2ec80b5 100644 --- a/main_test.go +++ b/main_test.go @@ -37,3 +37,26 @@ func TestDerivative(t *testing.T) { }) } } + +func TestGradientDescend(t *testing.T) { + for _, tc := range []struct { + name string + f func(float64) float64 + x0 float64 + alpha float64 + want float64 + }{ + {name: "constant", f: func(x float64) float64 { return 5 }, x0: 0.0, alpha: 0.01, want: 0.0}, + {name: "simple square", f: func(x float64) float64 { return x * x }, x0: 2.0, alpha: 0.01, want: 0.0}, + {name: "not so simple square", f: func(x float64) float64 { return x*x + 5*x - 3 }, x0: 1.0, alpha: 0.01, want: -2.5}, + {name: "diff.135 lpp. N18 a)", f: func(x float64) float64 { return 2*x*x - 3*x }, x0: 3, alpha: 0.01, want: 0.75}, + {name: "diff.135 lpp. N18 b)", f: func(x float64) float64 { return x*x - x + 2 }, x0: 0, alpha: 0.01, want: 0.5}, + } { + t.Run("", func(t *testing.T) { + got := GradientDescent(tc.f, tc.x0, tc.alpha) + if math.Abs(got-tc.want) > 0.0001 { + t.Errorf("Expected GradientDescent(%f, %f) to be %f, but got %f", tc.x0, tc.alpha, tc.want, got) + } + }) + } +} From c5ba5824ce38bff5264de2a89a88f0c7e5491105 Mon Sep 17 00:00:00 2001 From: Littleunidragon Date: Wed, 10 Jan 2024 20:01:41 +0200 Subject: [PATCH 4/4] no more snake film in main code --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index a4df3b6..1e9fb1f 100644 --- a/main.go +++ b/main.go @@ -47,7 +47,7 @@ func main() { var stop bool for !stop { time.Sleep(30 * time.Millisecond) - x -= 0.9 * derivative(f)(x) // CONTROL ALPHA HERE FOR GRAPHICS + x -= 0.1 * derivative(f)(x) // CONTROL ALPHA HERE FOR GRAPHICS // if you put alpha = 0.9, you wil get badly animated film about snake :) if derivative(f)(x) == 0 || math.Abs(derivative(f)(x)) < 0.00001 { stop = true