Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve multi stop plan unit generation #39

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions solution_move_stops_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

package nextroute

import (
"slices"
)

// SolutionMoveStopsGeneratorChannel generates all possible moves for a given
// vehicle and plan unit.
//
Expand Down Expand Up @@ -109,10 +113,13 @@ func SolutionMoveStopsGenerator(
}

// TODO: we can reuse the stopPositions slice from m
positions := make([]StopPosition, len(source))
positions := m.(*solutionMoveStopsImpl).stopPositions[:0]
positions = slices.Grow(positions, len(source))
for idx := range source {
positions[idx].stopIndex = source[idx].index
positions[idx].solution = source[idx].solution
positions = append(positions, StopPosition{
stopIndex: source[idx].index,
solution: source[idx].solution,
})
}

locations := make([]int, 0, len(source))
Expand Down
100 changes: 54 additions & 46 deletions solution_sequence_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package nextroute
import (
"math/rand"
"slices"
"sync/atomic"
)

// SequenceGeneratorChannel generates all possible sequences of solution stops
Expand All @@ -29,54 +28,55 @@ func SequenceGeneratorChannel(
pu SolutionPlanUnit,
quit <-chan struct{},
) chan SolutionStops {
planUnit := pu.(*solutionPlanStopsUnitImpl)
solution := planUnit.solution()
maxSequences := int64(solution.Model().SequenceSampleSize())
solutionStops := planUnit.SolutionStops()
ch := make(chan SolutionStops)
go func() {
defer close(ch)
switch planUnit.ModelPlanStopsUnit().NumberOfStops() {
case 1:
ch <- solutionStops
return
default:
used := make([]bool, len(solutionStops))
inDegree := map[int]int{}
modelPlanUnit := planUnit.ModelPlanUnit().(*planMultipleStopsImpl)
dag := modelPlanUnit.dag.(*directedAcyclicGraphImpl)
for _, solutionStop := range solutionStops {
inDegree[solutionStop.ModelStop().Index()] = 0
}
for _, arc := range dag.arcs {
inDegree[arc.Destination().Index()]++
sequenceGeneratorSync(pu, func(solutionStops SolutionStops) {
select {
case <-quit:
return
case ch <- slices.Clone(solutionStops):
}

sequenceGenerator(
solutionStops,
make([]SolutionStop, 0, len(solutionStops)),
used,
inDegree,
dag,
solution.Random(),
&maxSequences,
func(solutionStops SolutionStops) {
select {
case <-quit:
return
case ch <- solutionStops:
}
},
-1,
)
}
})
}()

return ch
}

func sequenceGenerator(
stops, sequence SolutionStops,
func sequenceGeneratorSync(pu SolutionPlanUnit, yield func(SolutionStops)) {
planUnit := pu.(*solutionPlanStopsUnitImpl)
solutionStops := planUnit.solutionStops
if planUnit.ModelPlanStopsUnit().NumberOfStops() == 1 {
yield(planUnit.SolutionStops())
return
}
solution := planUnit.solution()
maxSequences := int64(solution.Model().SequenceSampleSize())
nSolutionStops := len(solutionStops)
used := make([]bool, nSolutionStops)
inDegree := make(map[int]int, nSolutionStops)
modelPlanUnit := planUnit.ModelPlanUnit().(*planMultipleStopsImpl)
dag := modelPlanUnit.dag.(*directedAcyclicGraphImpl)
for _, arc := range dag.arcs {
inDegree[arc.Destination().Index()]++
}

recSequenceGenerator(
solutionStops,
make([]SolutionStop, 0, nSolutionStops),
used,
inDegree,
dag,
solution.Random(),
&maxSequences,
yield,
-1,
)
}

func recSequenceGenerator(
stops []SolutionStop,
sequence SolutionStops,
used []bool,
inDegree map[int]int,
dag DirectedAcyclicGraph,
Expand All @@ -85,21 +85,27 @@ func sequenceGenerator(
yield func(SolutionStops),
directSuccessor int,
) {
if len(sequence) == len(stops) {
if atomic.AddInt64(maxSequences, -1) >= 0 {
yield(slices.Clone(sequence))
nStops := len(stops)
if *maxSequences == 0 {
return
}
if len(sequence) == nStops {
*maxSequences--
if *maxSequences >= 0 {
yield(sequence)
}
return
}

stopOrder := random.Perm(len(stops))
stopOrder := random.Perm(nStops)

// we know the direct successor, so we move it to the front of the random
// sequence
if directSuccessor != -1 {
for _, stopIdx := range stopOrder {
if stops[stopIdx].Index() == directSuccessor {
stopOrder = []int{stopIdx}
stopOrder = stopOrder[:1]
stopOrder[0] = stopIdx
break
}
}
Expand Down Expand Up @@ -128,7 +134,9 @@ func sequenceGenerator(
}
}
}
sequenceGenerator(stops, append(sequence, stop), used, inDegree, dag, random, maxSequences, yield, directSuccessor)
recSequenceGenerator(
stops, append(sequence, stop), used, inDegree, dag, random, maxSequences, yield, directSuccessor,
)
// reached the maximum number of sequences
if *maxSequences == 0 {
return
Expand Down
6 changes: 2 additions & 4 deletions solution_vehicle.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,10 @@ func (v SolutionVehicle) bestMovePlanMultipleStops(
preAllocatedMoveContainer *PreAllocatedMoveContainer,
) SolutionMove {
var bestMove SolutionMove = newNotExecutableSolutionMoveStops(planUnit)
quitSequenceGenerator := make(chan struct{})
defer close(quitSequenceGenerator)
for sequence := range SequenceGeneratorChannel(planUnit, quitSequenceGenerator) {
sequenceGeneratorSync(planUnit, func(sequence SolutionStops) {
newMove := v.bestMoveSequence(ctx, planUnit, sequence, preAllocatedMoveContainer)
bestMove = takeBestInPlace(bestMove, newMove)
}
})
return bestMove
}

Expand Down
Loading