-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d0befdb
commit 6106464
Showing
19 changed files
with
508 additions
and
277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,65 @@ | ||
package gago | ||
|
||
// A Metric returns the distance between two genomes. | ||
type Metric func(a, b Individual) float64 | ||
|
||
// A DistanceMemoizer computes and stores Metric calculations. | ||
type DistanceMemoizer struct { | ||
Metric func(a, b Genome) float64 | ||
Distances map[Genome]map[Genome]float64 | ||
Metric Metric | ||
Distances map[string]map[string]float64 | ||
nCalculations int // Total number of calls to Metric for testing purposes | ||
} | ||
|
||
func makeDistanceMemoizer(metric func(a, b Genome) float64) DistanceMemoizer { | ||
// makeDistanceMemoizer initializes a DistanceMemoizer. | ||
func makeDistanceMemoizer(metric Metric) DistanceMemoizer { | ||
return DistanceMemoizer{ | ||
Metric: metric, | ||
Distances: make(map[Genome]map[Genome]float64), | ||
Distances: make(map[string]map[string]float64), | ||
} | ||
} | ||
|
||
func (dm *DistanceMemoizer) getDistance(a, b Genome) float64 { | ||
if dist, ok := dm.Distances[a][b]; ok { | ||
return dist | ||
// GetDistance returns the distance between two Individuals based on the | ||
// DistanceMemoizer's Metric field. If the two individuals share the same ID | ||
// then GetDistance returns 0. DistanceMemoizer stores the calculated distances | ||
// so that if GetDistance is called twice with the two same Individuals then | ||
// the second call will return the stored distance instead of recomputing it. | ||
func (dm *DistanceMemoizer) GetDistance(a, b Individual) float64 { | ||
// Check if the two individuals are the same before proceding | ||
if a.ID == b.ID { | ||
return 0 | ||
} | ||
// Create maps if the genomes have never been encountered | ||
if _, ok := dm.Distances[a.ID]; !ok { | ||
dm.Distances[a.ID] = make(map[string]float64) | ||
} else { | ||
// Check if the distance between the two genomes has been calculated | ||
if dist, ok := dm.Distances[a.ID][b.ID]; ok { | ||
return dist | ||
} | ||
} | ||
if dist, ok := dm.Distances[b][a]; ok { | ||
return dist | ||
if _, ok := dm.Distances[b.ID]; !ok { | ||
dm.Distances[b.ID] = make(map[string]float64) | ||
} else { | ||
if dist, ok := dm.Distances[b.ID][a.ID]; ok { | ||
return dist | ||
} | ||
} | ||
// Calculate the distance between the genomes and memoize it | ||
var dist = dm.Metric(a, b) | ||
dm.Distances[a][b] = dist | ||
dm.Distances[b][a] = dist | ||
dm.Distances[a.ID][b.ID] = dist | ||
dm.Distances[b.ID][a.ID] = dist | ||
dm.nCalculations++ | ||
return dist | ||
} | ||
|
||
// Return the average distance between a Individual and a slice of Individuals. | ||
func calcAvgDistances(indis Individuals, dm DistanceMemoizer) map[string]float64 { | ||
var avgDistances = make(map[string]float64) | ||
for _, a := range indis { | ||
for _, b := range indis { | ||
avgDistances[a.ID] += dm.GetDistance(a, b) | ||
} | ||
avgDistances[a.ID] /= float64(len(indis) - 1) | ||
} | ||
return avgDistances | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,50 @@ | ||
package gago | ||
|
||
import ( | ||
"math" | ||
"testing" | ||
) | ||
|
||
func L1Distance(x1, x2 Genome) (dist float64) { | ||
var g1 = x1.(Vector) | ||
var g2 = x2.(Vector) | ||
for i := range g1 { | ||
dist += math.Abs(g1[i] - g2[i]) | ||
} | ||
return | ||
} | ||
|
||
func TestDistanceMemoizer(t *testing.T) { | ||
var dm = makeDistanceMemoizer(L1Distance) | ||
var ( | ||
dm = makeDistanceMemoizer(l1Distance) | ||
a = Individual{Genome: Vector{1, 1, 1}, ID: "1"} | ||
b = Individual{Genome: Vector{3, 3, 3}, ID: "2"} | ||
c = Individual{Genome: Vector{6, 6, 6}, ID: "3"} | ||
) | ||
// Check the number of calculations is initially 0 | ||
if dm.nCalculations != 0 { | ||
t.Error("nCalculations is not initialized to 0") | ||
} | ||
// Check the distance between the 1st and itself | ||
if dm.GetDistance(a, a) != 0 { | ||
t.Error("Wrong calculated distance") | ||
} | ||
// Check the number of calculations is initially 0 | ||
if dm.nCalculations != 0 { | ||
t.Error("nCalculations should not have increased") | ||
} | ||
// Check the distance between the 1st and the 2nd individual | ||
if dm.GetDistance(a, b) != 6 { | ||
t.Error("Wrong calculated distance") | ||
} | ||
// Check the number of calculations has increased by 1 | ||
if dm.nCalculations != 1 { | ||
t.Error("nCalculations has not increased") | ||
} | ||
// Check the distance between the 2nd and the 1st individual | ||
if dm.GetDistance(b, a) != 6 { | ||
t.Error("Wrong calculated distance") | ||
} | ||
// Check the number of calculations has not increased | ||
if dm.nCalculations != 1 { | ||
t.Error("nCalculations has increased") | ||
} | ||
// Check the distance between the 1st and the 3rd individual | ||
if dm.GetDistance(a, c) != 15 { | ||
t.Error("Wrong calculated distance") | ||
} | ||
// Check the distance between the 1st and the 3rd individual | ||
if dm.GetDistance(b, c) != 9 { | ||
t.Error("Wrong calculated distance") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.