Skip to content

Commit

Permalink
adds flight optimizer/scorer and montecarlo.
Browse files Browse the repository at this point in the history
adds the flight optimizer and scorer interfaces.

the first returns the score and distance for a given flight, allowing
multiple implementations of the optimization algorithm (montecarlo,
simulated annealing, genetic, branch/bound, ...). actual implementation
based on montecarlo is included.

the second is the actual scoring interface, which takes a set of turn
points and returns the score points for the competition (netcoupe,
online contest, ...). netcoupe scoring implementation is included.

Fixes #48, #85.
  • Loading branch information
rochaporto committed Mar 9, 2015
1 parent 8be8388 commit bed55c5
Show file tree
Hide file tree
Showing 15 changed files with 33,574 additions and 93 deletions.
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Global struct {
Airfielder string
Flighter string
Waypointer string
Scorer string
}

// Config holds all the config information for ezgliding plugins and apps.
Expand Down
2 changes: 2 additions & 0 deletions ezgliding.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ airfielder=welt2000
flighter=netcoupe
# Plugins service waypoint related queries.
waypointer=welt2000
# Plugins serving flight scoring.
scorer=netcoupe

[web]
# port server listens to
Expand Down
114 changes: 114 additions & 0 deletions flight/montecarlo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2015 The ezgliding Authors.
//
// This file is part of ezgliding.
//
// ezgliding is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ezgliding is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ezgliding. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Ricardo Rocha <rocha.porto@gmail.com>

package flight

import (
"fmt"
"math/rand"

"github.com/rochaporto/ezgliding/spatial"
)

// Montecarlo is the montecarlo based implementation of the flight Optimizer.
type Montecarlo struct {
Cycles int
MCCycles int
MCGuesses int
}

// NewMontecarlo returns a new Montecarlo optimizer instance.
func NewMontecarlo() *Montecarlo {
return &Montecarlo{Cycles: 10, MCCycles: 100000, MCGuesses: 0}
}

// Optimize implements Optimizer().
func (mc *Montecarlo) Optimize(track []Point, nTP int, scorer Scorer) (OptResult, error) {
maxDistChan := make(chan float64, mc.Cycles)
maxChan := make(chan []int, mc.Cycles)

for c := 0; c < mc.Cycles; c++ {
go func() {
var candidate = make([]int, nTP)
var max = make([]int, nTP)
var cdistance float64
var maxDistance = 0.0

// start with uniform distribution (equal distance)
for i := 0; i < nTP; i++ {
candidate[i] = (len(track) / (nTP - 1)) * i
}
cdistance = mc.distance(track, candidate)

// run montecarlo cycles
var index, bwp, twp, nwp int
for i := 0; i < mc.MCCycles; i++ {
index = rand.Intn(nTP-2) + 1
bwp = candidate[0]
if index > 0 {
bwp = candidate[index-1]
}
twp = len(track) - 1
if index < nTP-1 {
twp = candidate[index+1]
}
nwp = rand.Intn(twp-bwp) + bwp
candidate[index] = nwp
//sort.Sort(sort.IntSlice(candidate))
cdistance = mc.distance(track, candidate)
if cdistance > maxDistance {
maxDistance = cdistance
max = candidate
}
}

maxDistChan <- maxDistance
maxChan <- max
}()
}

gMax := make([]int, nTP)
gMaxDistance := 0.0
var dist float64
var pts = make([]int, nTP)
for i := 0; i < mc.Cycles; i++ {
dist = <-maxDistChan
pts = <-maxChan
if dist > gMaxDistance {
gMaxDistance = dist
gMax = pts
}
}
fmt.Printf("%v :: %v\n", gMax, len(track))
result := OptResult{TurnPoints: make([]Point, nTP), Distance: gMaxDistance}
for i := 0; i < nTP; i++ {
result.TurnPoints[i] = track[gMax[i]]
}
return result, nil
}

func (mc *Montecarlo) distance(track []Point, tps []int) float64 {
distance := 0.0
for i := 0; i < len(tps)-1; i++ {
v := spatial.GCDistance(track[tps[i]].Latitude, track[tps[i]].Longitude,
track[tps[i+1]].Latitude, track[tps[i+1]].Longitude)
distance += v
}
return distance
}
83 changes: 83 additions & 0 deletions flight/montecarlo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2015 The ezgliding Authors.
//
// This file is part of ezgliding.
//
// ezgliding is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ezgliding is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ezgliding. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Ricardo Rocha <rocha.porto@gmail.com>

package flight

import (
"fmt"
"io/ioutil"
"net/http"
"testing"
)

type MontecarloTest struct {
t string
loc string
res Result
}

var montecarloTests = []MontecarloTest{
MontecarloTest{t: "sample flight", loc: "./t/sample-flight.igc", res: Result{Distance: 710000.0}},
MontecarloTest{t: "sample flight2", loc: "./t/sample-flight2.igc", res: Result{Distance: 710000.0}},
}

func TestMontecarlo(t *testing.T) {
mc := NewMontecarlo()
for _, test := range montecarloTests {
content, err := fetch(test.loc)
if err != nil {
t.Errorf("failed to load flight :: %v", err)
continue
}
f, err := ParseIGC(content)
if err != nil {
t.Errorf("failed to parse content :: %v", err)
continue
}
result, err := mc.Optimize(f.Points)
if err != nil {
t.Errorf("failed montecarlo optimize :: %v", err)
continue
}
fmt.Printf("%v\n%v\n", test.t, result.Distance)
for i, p := range result.TurnPoints {
fmt.Printf("data.setCell(%v, 0, %v);\n", i, p.Latitude)
fmt.Printf("data.setCell(%v, 1, %v);\n", i, p.Longitude)
fmt.Printf("data.setCell(%v, 2, 'WP%v');\n", i, i)
}
}
}

// FIXME: should be a common function in another package
func fetch(location string) (string, error) {
var content []byte
// case http
resp, err := http.Get(location)
if err == nil {
defer resp.Body.Close()
content, err = ioutil.ReadAll(resp.Body)
} else { // case file
resp, err := ioutil.ReadFile(location)
if err != nil {
return "", err
}
content = resp
}
return string(content), nil
}
48 changes: 48 additions & 0 deletions flight/optimizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2015 The ezgliding Authors.
//
// This file is part of ezgliding.
//
// ezgliding is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ezgliding is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ezgliding. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Ricardo Rocha <rocha.porto@gmail.com>

package flight

// Optimizer returns the optimized distance and score for the given track.
// nTP is the number of turnpoints to optimize for, and includes start and
// finish. This means 3 for out and return, 4 for triangle, ...
// The optimizer implementation will evaluation most or all combinations
// of track points building a set of valid TPs, and pass that to the scorer.
// The set of TPs with the higher score is returned, along with the actual
// distance between TPs.
type Optimizer interface {
Optimize(track []Point, nTP int, scorer Scorer) (OptResult, error)
}

// OptResult holds information about a given flight optimization.
type OptResult struct {
// TurnPoints is the set of turn points in this optimization.
TurnPoints []Point
// Distance is the total distance between turnpoints (direct line).
Distance float64
// Score is the number of points given by the Scorer implementation.
Score float64
// Description is a given task description by the Scorer implementation.
ScorerID string
}

// Optimizers holds a map of all available Optimizers, keyed on ID.
var Optimizers = map[string]Optimizer{
"montecarlo": Optimizer(NewMontecarlo()),
}
29 changes: 29 additions & 0 deletions flight/scorer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2015 The ezgliding Authors.
//
// This file is part of ezgliding.
//
// ezgliding is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ezgliding is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ezgliding. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Ricardo Rocha <rocha.porto@gmail.com>

package flight

// Scorer returns the score for the given set of turn points.
// The number of turn points given is variable and the resulting score will
// depend on it. As an example, the netcoupe scorer will take any of 1, 2 or
// 3 turn points (but not more), and for 2TP (triangle) will give a higher
// score if it is an FAI triangle (for the same total distance between TPs).
type Scorer interface {
Score(turnPoints []Point) (float64, error)
}
Loading

0 comments on commit bed55c5

Please sign in to comment.