-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from nextmv-io/merschformann/remove-alns-sdk-dep
Moves nextroute-only sdk packages to the repo
- Loading branch information
Showing
86 changed files
with
2,049 additions
and
111 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Description: This script adds a header to all go files that are missing it. | ||
import glob | ||
|
||
HEADER = "// © 2019-present nextmv.io inc" | ||
|
||
# List all go files in all subdirectories | ||
go_files = glob.glob("**/*.go", recursive=True) | ||
|
||
# Check if the header is the first line of each file | ||
missing = [] | ||
checked = 0 | ||
for file in go_files: | ||
with open(file, "r") as f: | ||
first_line = f.readline().strip() | ||
if first_line != HEADER: | ||
missing.append(file) | ||
checked += 1 | ||
|
||
# Add the header to all missing files | ||
for file in missing: | ||
print(f"Adding header to {file}") | ||
with open(file) as f: | ||
content = f.read() | ||
with open(file, "w") as f: | ||
f.write(HEADER + "\n\n" + content) | ||
|
||
print(f"Checked {checked} files, added header to {len(missing)} files") |
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 |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// © 2019-present nextmv.io inc | ||
|
||
package common | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
) | ||
|
||
// Alias is an interface that allows for sampling from a discrete | ||
// distribution in O(1) time. | ||
type Alias interface { | ||
Sample(rng *rand.Rand) int | ||
} | ||
|
||
// NewAlias creates a new Alias from the given weights. | ||
// The weights must be positive and at least one weight must be given. | ||
// The weights are normalized to sum to 1. | ||
// NewAlias([]float64{1, 2, 3}) will return an Alias that will | ||
// return 0 with probability 1/6, 1 with probability 1/3 and 2 with | ||
// probability 1/2. | ||
func NewAlias(weights []float64) (Alias, error) { | ||
n := len(weights) | ||
|
||
if n < 1 { | ||
return nil, fmt.Errorf( | ||
"at least one weight is required", | ||
) | ||
} | ||
|
||
if int(uint32(n)) != n { | ||
return nil, fmt.Errorf( | ||
"too many weights, max is %d", | ||
1<<32-1, | ||
) | ||
} | ||
|
||
sum := 0.0 | ||
|
||
for idx, weight := range weights { | ||
if weight <= 0 { | ||
return nil, | ||
fmt.Errorf("a weight at index %v is non-positive %v", | ||
idx, | ||
weight, | ||
) | ||
} | ||
sum += weight | ||
} | ||
|
||
alias := aliasImpl{ | ||
table: make([]int32PieceImpl, n), | ||
} | ||
|
||
twins := make([]float64PieceImpl, n) | ||
|
||
smallTop := -1 | ||
largeBottom := n | ||
|
||
multiplier := float64(n) / sum | ||
for i, weight := range weights { | ||
weight *= multiplier | ||
if weight >= 1 { | ||
largeBottom-- | ||
twins[largeBottom] = float64PieceImpl{ | ||
weight, | ||
uint32(i), | ||
} | ||
} else { | ||
smallTop++ | ||
twins[smallTop] = float64PieceImpl{ | ||
weight, | ||
uint32(i), | ||
} | ||
} | ||
} | ||
for smallTop >= 0 && largeBottom < n { | ||
l := twins[smallTop] | ||
smallTop-- | ||
|
||
t := twins[largeBottom] | ||
largeBottom++ | ||
|
||
alias.table[l.alias].probability = uint32(l.probability * (1<<31 - 1)) | ||
alias.table[l.alias].alias = t.alias | ||
|
||
t.probability = (t.probability + l.probability) - 1 | ||
|
||
if t.probability < 1 { | ||
smallTop++ | ||
twins[smallTop] = t | ||
} else { | ||
largeBottom-- | ||
twins[largeBottom] = t | ||
} | ||
} | ||
for i := n - 1; i >= largeBottom; i-- { | ||
alias.table[twins[i].alias].probability = 1<<31 - 1 | ||
} | ||
for i := 0; i <= smallTop; i++ { | ||
alias.table[twins[i].alias].probability = 1<<31 - 1 | ||
} | ||
return &alias, nil | ||
} | ||
|
||
func (a *aliasImpl) Sample(random *rand.Rand) int { | ||
ri := uint32(random.Int31()) | ||
w := ri % uint32(len(a.table)) | ||
if ri > a.table[w].probability { | ||
return int(a.table[w].alias) | ||
} | ||
return int(w) | ||
} | ||
|
||
type aliasImpl struct { | ||
table []int32PieceImpl | ||
} | ||
|
||
type float64PieceImpl struct { | ||
probability float64 | ||
alias uint32 | ||
} | ||
|
||
type int32PieceImpl struct { | ||
probability uint32 | ||
alias uint32 | ||
} |
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 |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// © 2019-present nextmv.io inc | ||
|
||
package common_test | ||
|
||
import ( | ||
"math" | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/nextmv-io/sdk/common" | ||
) | ||
|
||
const ( | ||
samples = 1_000_000 | ||
errorEpsilon = 0.001 | ||
) | ||
|
||
func TestAlias(t *testing.T) { | ||
testAlias(t, []float64{2, 2}, 22) | ||
testAlias(t, []float64{1, 2, 3}, 123) | ||
testAlias(t, []float64{6, 2, 1, 4, 2}, 62142) | ||
testAlias(t, []float64{1000, 1, 3, 10}, 10001310) | ||
} | ||
|
||
func testAlias(t *testing.T, weights []float64, seed int64) { | ||
sum := 0.0 | ||
for i := 0; i < len(weights); i++ { | ||
sum += weights[i] | ||
} | ||
alias, err := common.NewAlias(weights) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
random := rand.New(rand.NewSource(seed)) | ||
counts := make([]int64, len(weights)) | ||
|
||
for i := 0; i < samples; i++ { | ||
counts[alias.Sample(random)]++ | ||
} | ||
|
||
for i := 0; i < len(weights); i++ { | ||
count := float64(counts[i]) / samples | ||
if math.Abs(count-weights[i]/sum) > errorEpsilon { | ||
t.Errorf( | ||
"Counts did not match, got %v, expected %v, seed %v", | ||
count, | ||
weights[i]/sum, | ||
seed, | ||
) | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// © 2019-present nextmv.io inc | ||
|
||
package common | ||
|
||
// BoundingBox contains information about a box. | ||
type BoundingBox interface { | ||
// Maximum returns the maximum location of the bounding box. The right | ||
// lower corner of the bounding box. | ||
Maximum() Location | ||
// Minimum returns the minimum location of the bounding box. The left | ||
// upper corner of the bounding box. | ||
Minimum() Location | ||
// IsValid returns true if the bounding box is valid. A bounding box is | ||
// valid if the maximum location is greater than the minimum location. | ||
IsValid() bool | ||
// Width returns the width of the box. | ||
Width() Distance | ||
// Height returns the height of the box. | ||
Height() Distance | ||
} | ||
|
||
// NewInvalidBoundingBox returns an invalid bounding box. | ||
func NewInvalidBoundingBox() BoundingBox { | ||
return boundingBox{ | ||
maximum: NewInvalidLocation(), | ||
minimum: NewInvalidLocation(), | ||
} | ||
} | ||
|
||
// NewBoundingBox returns a bounding box for the given locations. | ||
func NewBoundingBox(locations Locations) BoundingBox { | ||
if len(locations) == 0 || !locations[0].IsValid() { | ||
return NewInvalidBoundingBox() | ||
} | ||
|
||
minLatitude := locations[0].Latitude() | ||
maxLatitude := locations[0].Latitude() | ||
minLongitude := locations[0].Longitude() | ||
maxLongitude := locations[0].Longitude() | ||
|
||
for idx := 1; idx < len(locations); idx++ { | ||
if !locations[idx].IsValid() { | ||
return NewInvalidBoundingBox() | ||
} | ||
latitude := locations[idx].Latitude() | ||
longitude := locations[idx].Longitude() | ||
if minLatitude > latitude { | ||
minLatitude = latitude | ||
} | ||
if maxLatitude < latitude { | ||
maxLatitude = latitude | ||
} | ||
if minLongitude > longitude { | ||
minLongitude = longitude | ||
} | ||
if maxLongitude < longitude { | ||
maxLongitude = longitude | ||
} | ||
} | ||
maxLocation, _ := NewLocation(maxLongitude, maxLatitude) | ||
minLocation, _ := NewLocation(minLongitude, minLatitude) | ||
return boundingBox{ | ||
maximum: maxLocation, | ||
minimum: minLocation, | ||
} | ||
} | ||
|
||
type boundingBox struct { | ||
maximum Location | ||
minimum Location | ||
} | ||
|
||
func (b boundingBox) Width() Distance { | ||
if !b.IsValid() { | ||
return NewDistance(0.0, Meters) | ||
} | ||
leftUpper, _ := NewLocation(b.minimum.Longitude(), b.minimum.Latitude()) | ||
rightUpper, _ := NewLocation(b.maximum.Longitude(), b.minimum.Latitude()) | ||
width, _ := Haversine(leftUpper, rightUpper) | ||
return width | ||
} | ||
|
||
func (b boundingBox) Height() Distance { | ||
if !b.IsValid() { | ||
return NewDistance(0.0, Meters) | ||
} | ||
leftUpper, _ := NewLocation(b.minimum.Longitude(), b.minimum.Latitude()) | ||
leftLower, _ := NewLocation(b.minimum.Longitude(), b.maximum.Latitude()) | ||
height, _ := Haversine(leftUpper, leftLower) | ||
return height | ||
} | ||
|
||
func (b boundingBox) IsValid() bool { | ||
return b.maximum.IsValid() && | ||
b.minimum.IsValid() && | ||
b.maximum.Longitude() >= b.minimum.Longitude() && | ||
b.maximum.Latitude() >= b.minimum.Latitude() | ||
} | ||
|
||
func (b boundingBox) Maximum() Location { | ||
return b.maximum | ||
} | ||
|
||
func (b boundingBox) Minimum() Location { | ||
return b.minimum | ||
} |
Oops, something went wrong.