From 4df6d4dd18c02af83cf1a013fc57a19a64446433 Mon Sep 17 00:00:00 2001 From: anyongjin Date: Fri, 31 May 2024 18:19:57 +0800 Subject: [PATCH 1/4] support max parallel goroutines --- bayesopt.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/bayesopt.go b/bayesopt.go index a8f2fce..c693a3a 100644 --- a/bayesopt.go +++ b/bayesopt.go @@ -1,6 +1,7 @@ package bayesopt import ( + "fmt" "sync" "github.com/pkg/errors" @@ -17,6 +18,8 @@ const ( DefaultRandomRounds = 5 // DefaultMinimize is the default value of minimize. DefaultMinimize = true + // DefaultParallel is the default value of max number goroutines run parallelly. + DefaultParallel = 5 NumRandPoints = 100000 NumGradPoints = 256 @@ -39,6 +42,7 @@ type Optimizer struct { exploration Exploration minimize bool barrierFunc BarrierFunc + parallel int running bool explorationErr error @@ -92,6 +96,13 @@ func WithBarrierFunc(bf BarrierFunc) OptimizerOption { } } +// WithParallel sets max number of running goroutines parallelly. +func WithParallel(num int) OptimizerOption { + return func(o *Optimizer) { + o.mu.parallel = num + } +} + // New creates a new optimizer with the specified optimizable parameters and // options. func New(params []Param, opts ...OptimizerOption) *Optimizer { @@ -104,6 +115,7 @@ func New(params []Param, opts ...OptimizerOption) *Optimizer { o.mu.rounds = DefaultRounds o.mu.exploration = DefaultExploration o.mu.minimize = DefaultMinimize + o.mu.parallel = DefaultParallel o.mu.barrierFunc = DefaultBarrierFunc o.updateNames("") @@ -112,6 +124,10 @@ func New(params []Param, opts ...OptimizerOption) *Optimizer { opt(o) } + if o.mu.parallel <= 0 { + panic(fmt.Sprintf("parallel must >= 1, current: %v", o.mu.parallel)) + } + return o } @@ -323,6 +339,7 @@ func (o *Optimizer) Optimize(f func(map[Param]float64) float64) (x map[Param]flo o.mu.running = true o.mu.Unlock() + guard := make(chan struct{}, o.mu.parallel) var wg sync.WaitGroup for { if !o.Running() { @@ -337,9 +354,13 @@ func (o *Optimizer) Optimize(f func(map[Param]float64) float64) (x map[Param]flo break } if parallel { + guard <- struct{}{} wg.Add(1) go func() { - defer wg.Done() + defer func() { + <-guard + wg.Done() + }() o.Log(x, f(x)) }() From 2a1341dc6a8c7552eab91d2650643a84eda7c9fe Mon Sep 17 00:00:00 2001 From: anyongjin Date: Mon, 30 Sep 2024 11:15:14 +0800 Subject: [PATCH 2/4] modify the package address for temporary reference --- README.md | 4 ++-- bayesopt.go | 2 +- bayesopt_test.go | 2 +- exploration.go | 2 +- go.mod | 2 +- gp/gp_test.go | 4 ++-- gp/plot/plot.go | 2 +- gp/plot/plot_test.go | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d354887..5c9e84f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# go-bayesopt [![Build Status](https://travis-ci.org/d4l3k/go-bayesopt.svg?branch=master)](https://travis-ci.org/d4l3k/go-bayesopt) [![GoDoc](https://godoc.org/github.com/d4l3k/go-bayesopt?status.svg)](https://godoc.org/github.com/d4l3k/go-bayesopt) +# go-bayesopt [![Build Status](https://travis-ci.org/d4l3k/go-bayesopt.svg?branch=master)](https://travis-ci.org/d4l3k/go-bayesopt) [![GoDoc](https://godoc.org/github.com/anyongjin/go-bayesopt?status.svg)](https://godoc.org/github.com/anyongjin/go-bayesopt) A library for doing Bayesian Optimization using Gaussian Processes (blackbox optimizer) in Go/Golang. @@ -15,7 +15,7 @@ import ( "log" "math" - "github.com/d4l3k/go-bayesopt" + "github.com/anyongjin/go-bayesopt" ) func main() { diff --git a/bayesopt.go b/bayesopt.go index c693a3a..6ce0b39 100644 --- a/bayesopt.go +++ b/bayesopt.go @@ -8,7 +8,7 @@ import ( "gonum.org/v1/gonum/optimize" - "github.com/d4l3k/go-bayesopt/gp" + "github.com/anyongjin/go-bayesopt/gp" ) const ( diff --git a/bayesopt_test.go b/bayesopt_test.go index 4147f7b..6e5dd2b 100644 --- a/bayesopt_test.go +++ b/bayesopt_test.go @@ -4,7 +4,7 @@ import ( "math" "testing" - "github.com/d4l3k/go-bayesopt/gp/plot" + "github.com/anyongjin/go-bayesopt/gp/plot" "gonum.org/v1/gonum/floats" ) diff --git a/exploration.go b/exploration.go index a03ee4b..888ff42 100644 --- a/exploration.go +++ b/exploration.go @@ -3,7 +3,7 @@ package bayesopt import ( "math" - "github.com/d4l3k/go-bayesopt/gp" + "github.com/anyongjin/go-bayesopt/gp" ) // Exploration is the strategy to use for exploring the Gaussian process. diff --git a/go.mod b/go.mod index 6854b62..f39c91b 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/d4l3k/go-bayesopt +module github.com/anyongjin/go-bayesopt go 1.12 diff --git a/gp/gp_test.go b/gp/gp_test.go index 98e43d6..22be4d3 100644 --- a/gp/gp_test.go +++ b/gp/gp_test.go @@ -7,8 +7,8 @@ import ( "gonum.org/v1/gonum/floats" - "github.com/d4l3k/go-bayesopt/gp" - "github.com/d4l3k/go-bayesopt/gp/plot" + "github.com/anyongjin/go-bayesopt/gp" + "github.com/anyongjin/go-bayesopt/gp/plot" ) func f(x, y float64) float64 { diff --git a/gp/plot/plot.go b/gp/plot/plot.go index 4780c40..a40e2f9 100644 --- a/gp/plot/plot.go +++ b/gp/plot/plot.go @@ -14,7 +14,7 @@ import ( "gonum.org/v1/gonum/floats" - "github.com/d4l3k/go-bayesopt/gp" + "github.com/anyongjin/go-bayesopt/gp" ) // SaveAll saves all dimension graphs of the gaussian process to a temp diff --git a/gp/plot/plot_test.go b/gp/plot/plot_test.go index a4a578c..948daa4 100644 --- a/gp/plot/plot_test.go +++ b/gp/plot/plot_test.go @@ -6,8 +6,8 @@ import ( "path" "testing" - "github.com/d4l3k/go-bayesopt/gp" - "github.com/d4l3k/go-bayesopt/gp/plot" + "github.com/anyongjin/go-bayesopt/gp" + "github.com/anyongjin/go-bayesopt/gp/plot" ) func TestPlot(t *testing.T) { From 7f3d3838fcae82f58f4301d1b718f8cb75d61469 Mon Sep 17 00:00:00 2001 From: anyongjin Date: Mon, 17 Mar 2025 10:05:43 +0800 Subject: [PATCH 3/4] Remove unnecessary dependencies --- bayesopt.go | 32 ++++---- bayesopt_test.go | 1 - go.mod | 2 - go.sum | 6 -- gp/gp_test.go | 4 - gp/plot/plot.go | 184 ------------------------------------------- gp/plot/plot_test.go | 38 --------- 7 files changed, 16 insertions(+), 251 deletions(-) delete mode 100644 gp/plot/plot.go delete mode 100644 gp/plot/plot_test.go diff --git a/bayesopt.go b/bayesopt.go index 6ce0b39..35f471d 100644 --- a/bayesopt.go +++ b/bayesopt.go @@ -54,11 +54,11 @@ type OptimizerOption func(*Optimizer) // WithOutputName sets the outputs name. Only really matters if you're planning // on using gp/plot. -func WithOutputName(name string) OptimizerOption { - return func(o *Optimizer) { - o.updateNames(name) - } -} +// func WithOutputName(name string) OptimizerOption { +// return func(o *Optimizer) { +// o.updateNames(name) +// } +// } // WithRandomRounds sets the number of random rounds to run. func WithRandomRounds(rounds int) OptimizerOption { @@ -118,7 +118,7 @@ func New(params []Param, opts ...OptimizerOption) *Optimizer { o.mu.parallel = DefaultParallel o.mu.barrierFunc = DefaultBarrierFunc - o.updateNames("") + //o.updateNames("") for _, opt := range opts { opt(o) @@ -132,16 +132,16 @@ func New(params []Param, opts ...OptimizerOption) *Optimizer { } // updateNames sets the gaussian process names. -func (o *Optimizer) updateNames(outputName string) { - o.mu.Lock() - defer o.mu.Unlock() - - var inputNames []string - for _, p := range o.mu.params { - inputNames = append(inputNames, p.GetName()) - } - o.mu.gp.SetNames(inputNames, outputName) -} +// func (o *Optimizer) updateNames(outputName string) { +// o.mu.Lock() +// defer o.mu.Unlock() + +// var inputNames []string +// for _, p := range o.mu.params { +// inputNames = append(inputNames, p.GetName()) +// } +// o.mu.gp.SetNames(inputNames, outputName) +// } // GP returns the underlying gaussian process. Primary for use with plotting // behavior. diff --git a/bayesopt_test.go b/bayesopt_test.go index 6e5dd2b..80f1679 100644 --- a/bayesopt_test.go +++ b/bayesopt_test.go @@ -4,7 +4,6 @@ import ( "math" "testing" - "github.com/anyongjin/go-bayesopt/gp/plot" "gonum.org/v1/gonum/floats" ) diff --git a/go.mod b/go.mod index f39c91b..8076444 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,6 @@ module github.com/anyongjin/go-bayesopt go 1.12 require ( - github.com/blend/go-sdk v2.0.0+incompatible // indirect github.com/pkg/errors v0.8.1 - github.com/wcharczuk/go-chart v2.0.1+incompatible gonum.org/v1/gonum v0.6.0 ) diff --git a/go.sum b/go.sum index f9e7342..a3acc54 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,13 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/blend/go-sdk v2.0.0+incompatible h1:FL9X/of4ZYO5D2JJNI4vHrbXPfuSDbUa7h8JP9+E92w= -github.com/blend/go-sdk v2.0.0+incompatible/go.mod h1:3GUb0YsHFNTJ6hsJTpzdmCUl05o8HisKjx5OAlzYKdw= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A= -github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e h1:Io7mpb+aUAGF0MKxbyQ7HQl1VgB+cL6ZJZUFaFNqVV4= diff --git a/gp/gp_test.go b/gp/gp_test.go index 22be4d3..407d8af 100644 --- a/gp/gp_test.go +++ b/gp/gp_test.go @@ -8,7 +8,6 @@ import ( "gonum.org/v1/gonum/floats" "github.com/anyongjin/go-bayesopt/gp" - "github.com/anyongjin/go-bayesopt/gp/plot" ) func f(x, y float64) float64 { @@ -28,9 +27,6 @@ func TestKnown(t *testing.T) { gpAdd(gp, rand.Float64()*2*math.Pi-math.Pi, rand.Float64()*2*math.Pi-math.Pi) } - if _, err := plot.SaveAll(gp); err != nil { - t.Fatal(err) - } mean, variance, err := gp.Estimate([]float64{0.25, 0.75}) if err != nil { t.Fatal(err) diff --git a/gp/plot/plot.go b/gp/plot/plot.go deleted file mode 100644 index a40e2f9..0000000 --- a/gp/plot/plot.go +++ /dev/null @@ -1,184 +0,0 @@ -package plot - -import ( - "fmt" - "io" - "io/ioutil" - "math" - "os" - "path" - "sort" - - "github.com/pkg/errors" - "github.com/wcharczuk/go-chart" - - "gonum.org/v1/gonum/floats" - - "github.com/anyongjin/go-bayesopt/gp" -) - -// SaveAll saves all dimension graphs of the gaussian process to a temp -// directory and prints their file names. -func SaveAll(gp *gp.GP) (string, error) { - dir, err := ioutil.TempDir("", "gp-plots") - if err != nil { - return "", err - } - dims := gp.Dims() - for i := 0; i < dims; i++ { - name := fmt.Sprintf("%d.svg", i) - fpath := path.Join(dir, name) - f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0755) - if err != nil { - return "", err - } - defer f.Close() - if err := GP(gp, f, i); err != nil { - return "", err - } - f.Close() - } - return dir, nil -} - -// GP renders a plot of the gaussian process for the specified dimension. -func GP(gp *gp.GP, w io.Writer, dim int) error { - dims := gp.Dims() - if dim >= dims { - return errors.Errorf("requested graph of dimension %d; only %d dimensions", dim, dims) - } - - inputs, outputs := gp.RawData() - - type pair struct { - x []float64 - y float64 - } - - pairs := make([]pair, len(inputs)) - for i := range pairs { - pairs[i].x = inputs[i] - pairs[i].y = outputs[i] - } - - sort.Slice(pairs, func(a, b int) bool { - return pairs[a].x[dim] < pairs[b].x[dim] - }) - - knownX := make([]float64, len(pairs)) - knownY := make([]float64, len(pairs)) - for i, p := range pairs { - knownX[i] = p.x[dim] - knownY[i] = p.y - } - - graph := chart.Chart{ - Title: fmt.Sprintf("%s vs. %s", gp.Name(dim), gp.OutputName()), - TitleStyle: chart.StyleShow(), - XAxis: chart.XAxis{ - Name: gp.Name(dim), - NameStyle: chart.StyleShow(), - Style: chart.StyleShow(), - }, - YAxis: chart.YAxis{ - Name: gp.OutputName(), - NameStyle: chart.StyleShow(), - Style: chart.StyleShow(), - }, - Background: chart.Style{ - Padding: chart.Box{ - Top: 20, - Left: 20, - Bottom: 20, - Right: 20, - }, - }, - } - graph.Elements = []chart.Renderable{ - chart.Legend(&graph), - } - - max := floats.Max(knownX) - min := floats.Min(knownX) - const steps = chart.DefaultChartWidth - x := make([]float64, 0, steps) - means := make([]float64, 0, steps) - uppers := make([]float64, 0, steps) - lowers := make([]float64, 0, steps) - stepSize := (max - min) / steps - - pairI := 0 - -outer: - for j := range x { - xi := stepSize*float64(j) + min - x = append(x, xi) - - var lowerPair, upperPair pair - for upperPair.x == nil || upperPair.x[dim] < xi { - if upperPair.x != nil { - pairI += 1 - } - if pairI+1 >= len(pairs) { - break outer - } - lowerPair = pairs[pairI] - upperPair = pairs[pairI+1] - } - - mid := (xi - lowerPair.x[dim]) / (upperPair.x[dim] - lowerPair.x[dim]) - args := make([]float64, dims) - floats.AddScaled(args, 1-mid, lowerPair.x) - floats.AddScaled(args, mid, upperPair.x) - mean, sd, err := gp.Estimate(args) - if err != nil { - return err - } - - if math.IsNaN(mean) || math.IsNaN(sd) { - continue - } - - means = append(means, mean) - uppers = append(uppers, mean+sd) - lowers = append(lowers, mean-sd) - } - - graph.Series = append( - graph.Series, - chart.ContinuousSeries{ - Name: "Mean", - XValues: x, - YValues: means, - }, - chart.ContinuousSeries{ - Name: "+1σ", - XValues: x, - YValues: uppers, - }, - chart.ContinuousSeries{ - Name: "-1σ", - XValues: x, - YValues: lowers, - }, - ) - - graph.Series = append( - graph.Series, - chart.ContinuousSeries{ - Name: "Known", - XValues: knownX, - YValues: knownY, - Style: chart.Style{ - Show: true, - StrokeWidth: chart.Disabled, - DotWidth: 5, - }, - }, - ) - - if err := graph.Render(chart.SVG, w); err != nil { - return err - } - return nil -} diff --git a/gp/plot/plot_test.go b/gp/plot/plot_test.go deleted file mode 100644 index 948daa4..0000000 --- a/gp/plot/plot_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package plot_test - -import ( - "fmt" - "os" - "path" - "testing" - - "github.com/anyongjin/go-bayesopt/gp" - "github.com/anyongjin/go-bayesopt/gp/plot" -) - -func TestPlot(t *testing.T) { - gp := gp.New(gp.MaternCov{}, 0) - gp.Add([]float64{1, 5}, 1) - gp.Add([]float64{2, 4}, 2) - gp.Add([]float64{3, 3}, 3) - gp.Add([]float64{4, 2}, 4) - gp.Add([]float64{5, 1}, 5) - gp.Add([]float64{10, -5}, 10) - - dir, err := plot.SaveAll(gp) - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - for i := 0; i < 2; i++ { - fpath := path.Join(dir, fmt.Sprintf("%d.svg", i)) - info, err := os.Stat(fpath) - if err != nil { - t.Fatal(err) - } - if info.Size() == 0 { - t.Errorf("%q: size = 0", fpath) - } - } -} From 2d50392d98e88ddb0ef0445a7bd19fc73f79b936 Mon Sep 17 00:00:00 2001 From: anyongjin Date: Mon, 17 Mar 2025 10:13:35 +0800 Subject: [PATCH 4/4] comment plot --- bayesopt_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bayesopt_test.go b/bayesopt_test.go index 80f1679..4a4815a 100644 --- a/bayesopt_test.go +++ b/bayesopt_test.go @@ -30,9 +30,9 @@ func TestOptimizer(t *testing.T) { x, y := o.GP().RawData() t.Logf("x %+v\ny %+v", x, y) } - if _, err := plot.SaveAll(o.GP()); err != nil { - t.Errorf("plot error: %+v", err) - } + // if _, err := plot.SaveAll(o.GP()); err != nil { + // t.Errorf("plot error: %+v", err) + // } { got := x[X] @@ -77,9 +77,9 @@ func TestOptimizerMax(t *testing.T) { x, y := o.GP().RawData() t.Logf("x %+v\ny %+v", x, y) } - if _, err := plot.SaveAll(o.GP()); err != nil { - t.Errorf("plot error: %+v", err) - } + // if _, err := plot.SaveAll(o.GP()); err != nil { + // t.Errorf("plot error: %+v", err) + // } { got := x[X] @@ -124,9 +124,9 @@ func TestOptimizerBounds(t *testing.T) { x, y := o.GP().RawData() t.Logf("x %+v\ny %+v", x, y) } - if _, err := plot.SaveAll(o.GP()); err != nil { - t.Errorf("plot error: %+v", err) - } + // if _, err := plot.SaveAll(o.GP()); err != nil { + // t.Errorf("plot error: %+v", err) + // } { got := x[X]