Skip to content

Instantly share code, notes, and snippets.

@getjump
Last active June 23, 2022 11:19
Show Gist options
  • Save getjump/483167422d30186d42690fa0c2db21f3 to your computer and use it in GitHub Desktop.
Save getjump/483167422d30186d42690fa0c2db21f3 to your computer and use it in GitHub Desktop.
Golang Particle Swarm Optimization runner for FreeFEM
package main
import (
"bytes"
"context"
"fmt"
"log"
"math"
"math/rand"
"os"
"os/exec"
"os/signal"
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
)
const DIMENSIONS = 2
const SIGMA0 = 1
type Position [DIMENSIONS]float64
type Velocity [DIMENSIONS]float64
type Minimum struct {
Lock sync.Mutex
Position
Value float64
}
type Particle struct {
Lock sync.Mutex
Position
Velocity
Minimum
}
type PSOParameters struct {
LowerLimits [DIMENSIONS]float64
UpperLimits [DIMENSIONS]float64
ParticlesAmount int
EpochsAmount int
W float64
C1 float64
C2 float64
counter uint64
}
type PSORuntime struct {
Minimum
Particles []Particle
}
func main() {
args := os.Args
ctx := context.Background()
ctx = context.WithValue(ctx, "file", args[1])
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-signals
os.Exit(0)
}()
rand.Seed(time.Now().Unix())
ParticleSwarmOptimization(ctx, PSOParameters{
EpochsAmount: 50,
ParticlesAmount: 25,
W: 0.4,
C1: 1,
C2: 1.5,
LowerLimits: [DIMENSIONS]float64{1e4 * SIGMA0, 1e6 * SIGMA0},
UpperLimits: [DIMENSIONS]float64{9e6 * SIGMA0, 5.9e7 * SIGMA0},
}, 16)
}
func (r *PSORuntime) Update(ctx context.Context, p *Particle, psp *PSOParameters, limiter chan interface{}) {
<-limiter
p.Lock.Lock()
defer p.Lock.Unlock()
d1 := rand.Float64()
d2 := rand.Float64()
r.Minimum.Lock.Lock()
for j := 0; j < DIMENSIONS; j++ {
p.Velocity[j] = psp.W*p.Velocity[j] + psp.C1*d1*(p.Minimum.Position[j]) + psp.C2*d2*(r.Minimum.Position[j]-p.Position[j])
p.Position[j] += p.Velocity[j]
}
r.Minimum.Lock.Unlock()
J := CalculateFunctional(ctx, ctx.Value("file").(string), p)
atomic.AddUint64(&psp.counter, 1)
if J < p.Minimum.Value {
copy(p.Minimum.Position[:], p.Position[:])
p.Minimum.Value = J
}
r.Minimum.Lock.Lock()
if J < r.Minimum.Value {
copy(r.Minimum.Position[:], p.Position[:])
r.Minimum.Value = J
fmt.Println(r.Minimum.Value, r.Minimum.Position)
}
r.Minimum.Lock.Unlock()
limiter <- struct{}{}
}
func ParticleSwarmOptimization(ctx context.Context, p PSOParameters, concurrency int) {
r := PSORuntime{
Minimum: Minimum{Value: math.MaxFloat64},
}
limiter := make(chan interface{}, concurrency)
r.Particles = make([]Particle, p.ParticlesAmount)
for i := 0; i < p.ParticlesAmount; i++ {
for j := 0; j < DIMENSIONS; j++ {
r.Particles[i].Position[j] = p.LowerLimits[j] + rand.Float64()*(p.UpperLimits[j]-p.LowerLimits[j])
r.Particles[i].Velocity[j] = 0
}
r.Particles[i].Minimum.Value = math.MaxFloat64
copy(r.Particles[i].Minimum.Position[:], r.Particles[i].Position[:])
}
var wg sync.WaitGroup
for e := 0; e < p.EpochsAmount; e++ {
for i := 0; i < p.ParticlesAmount; i++ {
wg.Add(1)
go func(v int) {
defer wg.Done()
r.Update(ctx, &r.Particles[v], &p, limiter)
}(i)
}
}
for i := 0; i < concurrency; i++ {
limiter <- struct{}{}
}
// for _ = range limiter {
// limiter <- struct{}{}
// }
wg.Wait()
fmt.Println(r.Minimum.Value, r.Minimum.Position)
}
func getDimensionsSlice(p *Particle) []string {
var result []string
for _, d := range p.Position {
result = append(result, fmt.Sprintf("%f", d))
}
return result
}
func CalculateFunctional(ctx context.Context, file string, p *Particle) float64 {
cmd := exec.CommandContext(ctx, "FreeFem++", append([]string{
file,
strconv.Itoa(DIMENSIONS),
}, getDimensionsSlice(p)...)...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
output := out.String()
lines := strings.Split(output, "\n")
var result float64
if result, err = strconv.ParseFloat(lines[len(lines)-1], 64); err != nil {
log.Fatal(err)
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment