-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathparticle_swarm.go
160 lines (149 loc) · 5.02 KB
/
particle_swarm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package hego
import (
"errors"
"fmt"
"math"
"math/rand"
"time"
)
// PSOResult represents the results of the particle swarm optimization
type PSOResult struct {
BestParticles [][]float64
BestObjectives []float64
BestParticle []float64
BestObjective float64
Result
}
// PSOSettings represents settings for the particle swarm optimization
type PSOSettings struct {
// PopulationSize is the number of particles
PopulationSize int
// LearningRate determines the movement size of each particle
LearningRate float64
// Omega is the weight of the current velocity, a momentum
Omega float64
// GlobalWeight determines how much a particle should drift towards the global optimum
GlobalWeight float64
// ParticleWeight determines how much a particle should drift towards the best known position of this particle
ParticleWeight float64
Settings
}
// Verify checks the validity of the settings and returns nil if everything is ok
func (s *PSOSettings) Verify() error {
if s.PopulationSize <= 1 {
return fmt.Errorf("population size must be greater than 1, got %v", s.PopulationSize)
}
if s.LearningRate <= 0.0 {
return fmt.Errorf("learning rate must be greater than 0, got %v", s.LearningRate)
}
if s.Omega < 0.0 {
return fmt.Errorf("omega must be greater than 0, got %v", s.Omega)
}
if s.GlobalWeight < 0.0 {
return fmt.Errorf("GlobalWeight must be greater than 0, got %v", s.GlobalWeight)
}
if s.ParticleWeight < 0.0 {
return fmt.Errorf("ParticleWeight must be greater than 0, got %v", s.ParticleWeight)
}
if s.ParticleWeight == 0.0 && s.GlobalWeight == 0.0 {
return errors.New("when ParticleWeight and GlobalWeight are set to 0, the velocity will not change at all")
}
return nil
}
// PSO performs particle swarm optimization. Objective is the function to minimize, init initializes a tupe of particle and velocity, settings holds algorithm settings
func PSO(
objective func(x []float64) float64,
init func() ([]float64, []float64),
settings PSOSettings) (res PSOResult, err error) {
err = settings.Verify()
if err != nil {
err = fmt.Errorf("settings verification failed: %v", err)
return res, err
}
start := time.Now()
logger := newLogger("Particle Swarm Optimization", []string{"Iteration", "Population Mean", "Population Best"}, settings.Verbose, settings.MaxIterations)
// increase funcEvaluations counter for every call to objective
evaluate := func(x []float64) float64 {
res.FuncEvaluations++
return objective(x)
}
if settings.KeepHistory {
res.BestParticles = make([][]float64, 0, settings.MaxIterations)
res.BestObjectives = make([]float64, 0, settings.MaxIterations)
}
res.BestObjective = math.MaxFloat64
res.BestParticle = make([]float64, 0)
// initialize population with velocities and best known positions
particles := make([][]float64, settings.PopulationSize)
velocities := make([][]float64, settings.PopulationSize)
bestPositions := make([][]float64, settings.PopulationSize)
bestObjs := make([]float64, settings.PopulationSize)
globalBest := make([]float64, 0)
globalBestObj := math.MaxFloat64
for i := range particles {
particles[i], velocities[i] = init()
bestObjs[i] = evaluate(particles[i])
bestPositions[i] = make([]float64, len(particles[i]))
copy(bestPositions[i], particles[i])
if bestObjs[i] < globalBestObj {
globalBest = make([]float64, len(particles[i]))
copy(globalBest, particles[i])
globalBestObj = bestObjs[i]
}
}
if settings.KeepHistory {
res.BestObjectives = append(res.BestObjectives, globalBestObj)
res.BestParticles = append(res.BestParticles, globalBest)
}
for i := 0; i < settings.MaxIterations; i++ {
totalObj := 0.0
newGlobalBest := false
newGlobalBestParticle := make([]float64, len(globalBest))
for j, particle := range particles {
velocity := velocities[j]
for d, v := range velocity {
rp, rg := rand.Float64(), rand.Float64()
w := settings.Omega
phip, phig := settings.ParticleWeight, settings.GlobalWeight
velocity[d] = w*v + phip*rp*(bestPositions[j][d]-particle[d]) + phig*rg*(globalBest[d]-particle[d])
}
for d, p := range particle {
particle[d] = p + settings.LearningRate*velocity[d]
}
obj := evaluate(particle)
if obj < bestObjs[j] {
copy(bestPositions[j], particle)
bestObjs[j] = obj
if obj < globalBestObj {
newGlobalBest = true
copy(newGlobalBestParticle, particle)
copy(globalBest, particle)
globalBestObj = obj
}
}
totalObj += obj
}
if newGlobalBest {
next := make([]float64, len(globalBest))
copy(next, globalBest)
res.BestObjective = globalBestObj
res.BestParticle = next
if settings.KeepHistory {
res.BestParticles = append(res.BestParticles, next)
res.BestObjectives = append(res.BestObjectives, globalBestObj)
}
}
logger.AddLine(i, []string{
fmt.Sprint(i),
fmt.Sprint(totalObj / float64(settings.PopulationSize)),
fmt.Sprint(globalBestObj),
})
}
res.Runtime = time.Since(start)
res.Iterations = settings.MaxIterations
logger.Flush()
if settings.Verbose > 0 {
fmt.Printf("Done after %v!\n", res.Runtime)
}
return res, nil
}