Skip to content

Commit 9e434a2

Browse files
Merge pull request #39 from nextmv-io/ds/chore/fast-generator
Improve multi stop plan unit generation
2 parents 9a6eb7b + fea727c commit 9e434a2

File tree

3 files changed

+66
-54
lines changed

3 files changed

+66
-54
lines changed

solution_move_stops_generator.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
package nextroute
44

5+
import (
6+
"slices"
7+
)
8+
59
// SolutionMoveStopsGeneratorChannel generates all possible moves for a given
610
// vehicle and plan unit.
711
//
@@ -108,11 +112,13 @@ func SolutionMoveStopsGenerator(
108112
return
109113
}
110114

111-
// TODO: we can reuse the stopPositions slice from m
112-
positions := make([]StopPosition, len(source))
115+
positions := m.(*solutionMoveStopsImpl).stopPositions[:0]
116+
positions = slices.Grow(positions, len(source))
113117
for idx := range source {
114-
positions[idx].stopIndex = source[idx].index
115-
positions[idx].solution = source[idx].solution
118+
positions = append(positions, StopPosition{
119+
stopIndex: source[idx].index,
120+
solution: source[idx].solution,
121+
})
116122
}
117123

118124
locations := make([]int, 0, len(source))

solution_sequence_generator.go

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package nextroute
55
import (
66
"math/rand"
77
"slices"
8-
"sync/atomic"
98
)
109

1110
// SequenceGeneratorChannel generates all possible sequences of solution stops
@@ -29,54 +28,55 @@ func SequenceGeneratorChannel(
2928
pu SolutionPlanUnit,
3029
quit <-chan struct{},
3130
) chan SolutionStops {
32-
planUnit := pu.(*solutionPlanStopsUnitImpl)
33-
solution := planUnit.solution()
34-
maxSequences := int64(solution.Model().SequenceSampleSize())
35-
solutionStops := planUnit.SolutionStops()
3631
ch := make(chan SolutionStops)
3732
go func() {
3833
defer close(ch)
39-
switch planUnit.ModelPlanStopsUnit().NumberOfStops() {
40-
case 1:
41-
ch <- solutionStops
42-
return
43-
default:
44-
used := make([]bool, len(solutionStops))
45-
inDegree := map[int]int{}
46-
modelPlanUnit := planUnit.ModelPlanUnit().(*planMultipleStopsImpl)
47-
dag := modelPlanUnit.dag.(*directedAcyclicGraphImpl)
48-
for _, solutionStop := range solutionStops {
49-
inDegree[solutionStop.ModelStop().Index()] = 0
50-
}
51-
for _, arc := range dag.arcs {
52-
inDegree[arc.Destination().Index()]++
34+
sequenceGeneratorSync(pu, func(solutionStops SolutionStops) {
35+
select {
36+
case <-quit:
37+
return
38+
case ch <- slices.Clone(solutionStops):
5339
}
54-
55-
sequenceGenerator(
56-
solutionStops,
57-
make([]SolutionStop, 0, len(solutionStops)),
58-
used,
59-
inDegree,
60-
dag,
61-
solution.Random(),
62-
&maxSequences,
63-
func(solutionStops SolutionStops) {
64-
select {
65-
case <-quit:
66-
return
67-
case ch <- solutionStops:
68-
}
69-
},
70-
-1,
71-
)
72-
}
40+
})
7341
}()
7442

7543
return ch
7644
}
7745

78-
func sequenceGenerator(
79-
stops, sequence SolutionStops,
46+
func sequenceGeneratorSync(pu SolutionPlanUnit, yield func(SolutionStops)) {
47+
planUnit := pu.(*solutionPlanStopsUnitImpl)
48+
solutionStops := planUnit.solutionStops
49+
if planUnit.ModelPlanStopsUnit().NumberOfStops() == 1 {
50+
yield(planUnit.SolutionStops())
51+
return
52+
}
53+
solution := planUnit.solution()
54+
maxSequences := int64(solution.Model().SequenceSampleSize())
55+
nSolutionStops := len(solutionStops)
56+
used := make([]bool, nSolutionStops)
57+
inDegree := make(map[int]int, nSolutionStops)
58+
modelPlanUnit := planUnit.ModelPlanUnit().(*planMultipleStopsImpl)
59+
dag := modelPlanUnit.dag.(*directedAcyclicGraphImpl)
60+
for _, arc := range dag.arcs {
61+
inDegree[arc.Destination().Index()]++
62+
}
63+
64+
recursiveSequenceGenerator(
65+
solutionStops,
66+
make([]SolutionStop, 0, nSolutionStops),
67+
used,
68+
inDegree,
69+
dag,
70+
solution.Random(),
71+
&maxSequences,
72+
yield,
73+
-1,
74+
)
75+
}
76+
77+
func recursiveSequenceGenerator(
78+
stops []SolutionStop,
79+
sequence SolutionStops,
8080
used []bool,
8181
inDegree map[int]int,
8282
dag DirectedAcyclicGraph,
@@ -85,21 +85,27 @@ func sequenceGenerator(
8585
yield func(SolutionStops),
8686
directSuccessor int,
8787
) {
88-
if len(sequence) == len(stops) {
89-
if atomic.AddInt64(maxSequences, -1) >= 0 {
90-
yield(slices.Clone(sequence))
88+
if *maxSequences == 0 {
89+
return
90+
}
91+
nStops := len(stops)
92+
if len(sequence) == nStops {
93+
*maxSequences--
94+
if *maxSequences >= 0 {
95+
yield(sequence)
9196
}
9297
return
9398
}
9499

95-
stopOrder := random.Perm(len(stops))
100+
stopOrder := random.Perm(nStops)
96101

97102
// we know the direct successor, so we move it to the front of the random
98103
// sequence
99104
if directSuccessor != -1 {
100105
for _, stopIdx := range stopOrder {
101106
if stops[stopIdx].Index() == directSuccessor {
102-
stopOrder = []int{stopIdx}
107+
stopOrder = stopOrder[:1]
108+
stopOrder[0] = stopIdx
103109
break
104110
}
105111
}
@@ -128,7 +134,9 @@ func sequenceGenerator(
128134
}
129135
}
130136
}
131-
sequenceGenerator(stops, append(sequence, stop), used, inDegree, dag, random, maxSequences, yield, directSuccessor)
137+
recursiveSequenceGenerator(
138+
stops, append(sequence, stop), used, inDegree, dag, random, maxSequences, yield, directSuccessor,
139+
)
132140
// reached the maximum number of sequences
133141
if *maxSequences == 0 {
134142
return

solution_vehicle.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,10 @@ func (v SolutionVehicle) bestMovePlanMultipleStops(
316316
preAllocatedMoveContainer *PreAllocatedMoveContainer,
317317
) SolutionMove {
318318
var bestMove SolutionMove = newNotExecutableSolutionMoveStops(planUnit)
319-
quitSequenceGenerator := make(chan struct{})
320-
defer close(quitSequenceGenerator)
321-
for sequence := range SequenceGeneratorChannel(planUnit, quitSequenceGenerator) {
319+
sequenceGeneratorSync(planUnit, func(sequence SolutionStops) {
322320
newMove := v.bestMoveSequence(ctx, planUnit, sequence, preAllocatedMoveContainer)
323321
bestMove = takeBestInPlace(bestMove, newMove)
324-
}
322+
})
325323
return bestMove
326324
}
327325

0 commit comments

Comments
 (0)