From ccd349008eb7c611d690c4dd1fd9bca74b86ceb1 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 22 Sep 2016 21:04:54 -0400 Subject: [PATCH] parallelize --- primitive/ellipse.go | 34 +++++++++++++----------- primitive/model.go | 60 +++++++++++++++++++++++++++++++----------- primitive/rectangle.go | 56 +++++++++++++++++++++------------------ primitive/state.go | 19 ++++++++----- primitive/triangle.go | 32 +++++++++++----------- 5 files changed, 123 insertions(+), 78 deletions(-) diff --git a/primitive/ellipse.go b/primitive/ellipse.go index 442710d..e061247 100644 --- a/primitive/ellipse.go +++ b/primitive/ellipse.go @@ -13,21 +13,22 @@ type Ellipse struct { X, Y int Rx, Ry int Circle bool + rnd *rand.Rand } -func NewRandomEllipse(w, h int) *Ellipse { - x := rand.Intn(w) - y := rand.Intn(h) - rx := rand.Intn(w / 2) - ry := rand.Intn(h / 2) - return &Ellipse{w, h, x, y, rx, ry, false} +func NewRandomEllipse(w, h int, rnd *rand.Rand) *Ellipse { + x := rnd.Intn(w) + y := rnd.Intn(h) + rx := rnd.Intn(w / 2) + ry := rnd.Intn(h / 2) + return &Ellipse{w, h, x, y, rx, ry, false, rnd} } -func NewRandomCircle(w, h int) *Ellipse { - x := rand.Intn(w) - y := rand.Intn(h) - r := rand.Intn(w / 4) - return &Ellipse{w, h, x, y, r, r, true} +func NewRandomCircle(w, h int, rnd *rand.Rand) *Ellipse { + x := rnd.Intn(w) + y := rnd.Intn(h) + r := rnd.Intn(w / 4) + return &Ellipse{w, h, x, y, r, r, true, rnd} } func (c *Ellipse) Draw(dc *gg.Context) { @@ -46,17 +47,18 @@ func (c *Ellipse) Copy() Shape { } func (c *Ellipse) Mutate() { - switch rand.Intn(3) { + rnd := c.rnd + switch rnd.Intn(3) { case 0: - c.X = clampInt(c.X+rand.Intn(21)-10, 0, c.W-1) - c.Y = clampInt(c.Y+rand.Intn(21)-10, 0, c.H-1) + c.X = clampInt(c.X+rnd.Intn(21)-10, 0, c.W-1) + c.Y = clampInt(c.Y+rnd.Intn(21)-10, 0, c.H-1) case 1: - c.Rx = clampInt(c.Rx+rand.Intn(21)-10, 0, c.W-1) + c.Rx = clampInt(c.Rx+rnd.Intn(21)-10, 0, c.W-1) if c.Circle { c.Ry = c.Rx } case 2: - c.Ry = clampInt(c.Ry+rand.Intn(21)-10, 0, c.W-1) + c.Ry = clampInt(c.Ry+rnd.Intn(21)-10, 0, c.W-1) if c.Circle { c.Rx = c.Ry } diff --git a/primitive/model.go b/primitive/model.go index b732b06..de0e0de 100644 --- a/primitive/model.go +++ b/primitive/model.go @@ -5,6 +5,7 @@ import ( "image" "math/rand" "strings" + "time" "github.com/fogleman/gg" ) @@ -119,18 +120,47 @@ func (model *Model) Add(shape Shape) { } func (model *Model) Step() { - state := model.BestHillClimbState(model.Buffer, model.Mode, 100, 100, 10) - // state := model.BestRandomState(model.Buffer, model.Mode, 1000) - // state = Anneal(state, 0.1, 0.00001, 25000).(*State) - state = HillClimb(state, 1000).(*State) + state := model.runWorkers(model.Mode, 100, 100, 10) + // state := model.BestHillClimbState(model.Buffer, model.Mode, 100, 100, 20) + // state = HillClimb(state, 1000).(*State) model.Add(state.Shape) } -func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int) *State { +func (model *Model) runWorkers(t Mode, n, age, m int) *State { + wn := 1 //runtime.GOMAXPROCS(0) + ch := make(chan *State, wn) + wm := m / wn + if m%wn != 0 { + wm++ + } + for i := 0; i < wn; i++ { + go model.runWorker(t, n, age, wm, ch) + } + var bestEnergy float64 + var bestState *State + for i := 0; i < wn; i++ { + state := <-ch + energy := state.Energy() + if i == 0 || energy < bestEnergy { + bestEnergy = energy + bestState = state + } + } + return HillClimb(bestState, 1000).(*State) +} + +func (model *Model) runWorker(t Mode, n, age, m int, ch chan *State) { + buffer := image.NewRGBA(model.Target.Bounds()) + rnd := rand.New(rand.NewSource(time.Now().UnixNano())) + state := model.BestHillClimbState(buffer, t, n, age, m, rnd) + ch <- state +} + +func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int, rnd *rand.Rand) *State { var bestEnergy float64 var bestState *State for i := 0; i < m; i++ { - state := model.BestRandomState(buffer, t, n) + state := model.BestRandomState(buffer, t, n, rnd) before := state.Energy() state = HillClimb(state, age).(*State) energy := state.Energy() @@ -143,11 +173,11 @@ func (model *Model) BestHillClimbState(buffer *image.RGBA, t Mode, n, age, m int return bestState } -func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int) *State { +func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int, rnd *rand.Rand) *State { var bestEnergy float64 var bestState *State for i := 0; i < n; i++ { - state := model.RandomState(buffer, t) + state := model.RandomState(buffer, t, rnd) energy := state.Energy() if i == 0 || energy < bestEnergy { bestEnergy = energy @@ -157,20 +187,20 @@ func (model *Model) BestRandomState(buffer *image.RGBA, t Mode, n int) *State { return bestState } -func (model *Model) RandomState(buffer *image.RGBA, t Mode) *State { +func (model *Model) RandomState(buffer *image.RGBA, t Mode, rnd *rand.Rand) *State { switch t { default: - return model.RandomState(buffer, Mode(rand.Intn(5)+1)) + return model.RandomState(buffer, Mode(rnd.Intn(5)+1), rnd) case ModeTriangle: - return NewState(model, buffer, NewRandomTriangle(model.W, model.H)) + return NewState(model, buffer, NewRandomTriangle(model.W, model.H, rnd)) case ModeRectangle: - return NewState(model, buffer, NewRandomRectangle(model.W, model.H)) + return NewState(model, buffer, NewRandomRectangle(model.W, model.H, rnd)) case ModeEllipse: - return NewState(model, buffer, NewRandomEllipse(model.W, model.H)) + return NewState(model, buffer, NewRandomEllipse(model.W, model.H, rnd)) case ModeCircle: - return NewState(model, buffer, NewRandomCircle(model.W, model.H)) + return NewState(model, buffer, NewRandomCircle(model.W, model.H, rnd)) case ModeRotatedRectangle: - return NewState(model, buffer, NewRandomRotatedRectangle(model.W, model.H)) + return NewState(model, buffer, NewRandomRotatedRectangle(model.W, model.H, rnd)) } } diff --git a/primitive/rectangle.go b/primitive/rectangle.go index a0afa0a..79904f2 100644 --- a/primitive/rectangle.go +++ b/primitive/rectangle.go @@ -12,14 +12,15 @@ type Rectangle struct { W, H int X1, Y1 int X2, Y2 int + rnd *rand.Rand } -func NewRandomRectangle(w, h int) *Rectangle { - x1 := rand.Intn(w) - y1 := rand.Intn(h) - x2 := rand.Intn(w) - y2 := rand.Intn(h) - return &Rectangle{w, h, x1, y1, x2, y2} +func NewRandomRectangle(w, h int, rnd *rand.Rand) *Rectangle { + x1 := rnd.Intn(w) + y1 := rnd.Intn(h) + x2 := rnd.Intn(w) + y2 := rnd.Intn(h) + return &Rectangle{w, h, x1, y1, x2, y2, rnd} } func (r *Rectangle) bounds() (x1, y1, x2, y2 int) { @@ -54,13 +55,14 @@ func (r *Rectangle) Copy() Shape { } func (r *Rectangle) Mutate() { - switch rand.Intn(2) { + rnd := r.rnd + switch rnd.Intn(2) { case 0: - r.X1 = clampInt(r.X1+rand.Intn(21)-10, 0, r.W-1) - r.Y1 = clampInt(r.Y1+rand.Intn(21)-10, 0, r.H-1) + r.X1 = clampInt(r.X1+rnd.Intn(21)-10, 0, r.W-1) + r.Y1 = clampInt(r.Y1+rnd.Intn(21)-10, 0, r.H-1) case 1: - r.X2 = clampInt(r.X2+rand.Intn(21)-10, 0, r.W-1) - r.Y2 = clampInt(r.Y2+rand.Intn(21)-10, 0, r.H-1) + r.X2 = clampInt(r.X2+rnd.Intn(21)-10, 0, r.W-1) + r.Y2 = clampInt(r.Y2+rnd.Intn(21)-10, 0, r.H-1) } } @@ -80,15 +82,16 @@ type RotatedRectangle struct { X, Y int Sx, Sy int Angle int + rnd *rand.Rand } -func NewRandomRotatedRectangle(w, h int) *RotatedRectangle { - x := rand.Intn(w) - y := rand.Intn(h) - sx := rand.Intn(w / 2) - sy := rand.Intn(h / 2) - a := rand.Intn(360) - r := &RotatedRectangle{w, h, x, y, sx, sy, a} +func NewRandomRotatedRectangle(w, h int, rnd *rand.Rand) *RotatedRectangle { + x := rnd.Intn(w) + y := rnd.Intn(h) + sx := rnd.Intn(w / 2) + sy := rnd.Intn(h / 2) + a := rnd.Intn(360) + r := &RotatedRectangle{w, h, x, y, sx, sy, a, rnd} r.Mutate() return r } @@ -114,19 +117,20 @@ func (r *RotatedRectangle) Copy() Shape { } func (r *RotatedRectangle) Mutate() { - switch rand.Intn(3) { + rnd := r.rnd + switch rnd.Intn(3) { case 0: - r.X = clampInt(r.X+rand.Intn(21)-10, 0, r.W-1) - r.Y = clampInt(r.Y+rand.Intn(21)-10, 0, r.H-1) + r.X = clampInt(r.X+rnd.Intn(21)-10, 0, r.W-1) + r.Y = clampInt(r.Y+rnd.Intn(21)-10, 0, r.H-1) case 1: - r.Sx = clampInt(r.Sx+rand.Intn(21)-10, 0, r.W-1) - r.Sy = clampInt(r.Sy+rand.Intn(21)-10, 0, r.H-1) + r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1) + r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1) case 2: - r.Angle = r.Angle + rand.Intn(41) - 20 + r.Angle = r.Angle + rnd.Intn(41) - 20 } for !r.Valid() { - r.Sx = clampInt(r.Sx+rand.Intn(21)-10, 0, r.W-1) - r.Sy = clampInt(r.Sy+rand.Intn(21)-10, 0, r.H-1) + r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1) + r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1) } } diff --git a/primitive/state.go b/primitive/state.go index 7ac4f52..996e58b 100644 --- a/primitive/state.go +++ b/primitive/state.go @@ -6,26 +6,33 @@ type State struct { Model *Model Buffer *image.RGBA Shape Shape + Score float64 } func NewState(model *Model, buffer *image.RGBA, shape Shape) *State { - return &State{model, buffer, shape} + return &State{model, buffer, shape, -1} } func (state *State) Energy() float64 { - return state.Model.Energy(state.Shape, state.Buffer) + if state.Score < 0 { + state.Score = state.Model.Energy(state.Shape, state.Buffer) + } + return state.Score } func (state *State) DoMove() interface{} { - oldShape := state.Shape.Copy() + oldState := state.Copy() state.Shape.Mutate() - return oldShape + state.Score = -1 + return oldState } func (state *State) UndoMove(undo interface{}) { - state.Shape = undo.(Shape) + oldState := undo.(*State) + state.Shape = oldState.Shape + state.Score = oldState.Score } func (state *State) Copy() Annealable { - return &State{state.Model, state.Buffer, state.Shape.Copy()} + return &State{state.Model, state.Buffer, state.Shape.Copy(), state.Score} } diff --git a/primitive/triangle.go b/primitive/triangle.go index e5c477f..3e24c3f 100644 --- a/primitive/triangle.go +++ b/primitive/triangle.go @@ -13,16 +13,17 @@ type Triangle struct { X1, Y1 int X2, Y2 int X3, Y3 int + rnd *rand.Rand } -func NewRandomTriangle(w, h int) *Triangle { - x1 := rand.Intn(w) - y1 := rand.Intn(h) - x2 := rand.Intn(w) - y2 := rand.Intn(h) - x3 := rand.Intn(w) - y3 := rand.Intn(h) - t := &Triangle{w, h, x1, y1, x2, y2, x3, y3} +func NewRandomTriangle(w, h int, rnd *rand.Rand) *Triangle { + x1 := rnd.Intn(w) + y1 := rnd.Intn(h) + x2 := rnd.Intn(w) + y2 := rnd.Intn(h) + x3 := rnd.Intn(w) + y3 := rnd.Intn(h) + t := &Triangle{w, h, x1, y1, x2, y2, x3, y3, rnd} t.Mutate() return t } @@ -46,17 +47,18 @@ func (t *Triangle) Copy() Shape { } func (t *Triangle) Mutate() { + rnd := t.rnd for { - switch rand.Intn(3) { + switch rnd.Intn(3) { case 0: - t.X1 = clampInt(t.X1+rand.Intn(21)-10, 0, t.W-1) - t.Y1 = clampInt(t.Y1+rand.Intn(21)-10, 0, t.H-1) + t.X1 = clampInt(t.X1+rnd.Intn(21)-10, 0, t.W-1) + t.Y1 = clampInt(t.Y1+rnd.Intn(21)-10, 0, t.H-1) case 1: - t.X2 = clampInt(t.X2+rand.Intn(21)-10, 0, t.W-1) - t.Y2 = clampInt(t.Y2+rand.Intn(21)-10, 0, t.H-1) + t.X2 = clampInt(t.X2+rnd.Intn(21)-10, 0, t.W-1) + t.Y2 = clampInt(t.Y2+rnd.Intn(21)-10, 0, t.H-1) case 2: - t.X3 = clampInt(t.X3+rand.Intn(21)-10, 0, t.W-1) - t.Y3 = clampInt(t.Y3+rand.Intn(21)-10, 0, t.H-1) + t.X3 = clampInt(t.X3+rnd.Intn(21)-10, 0, t.W-1) + t.Y3 = clampInt(t.Y3+rnd.Intn(21)-10, 0, t.H-1) } if t.Valid() { break