Skip to content

Commit 9aedff3

Browse files
committed
simplex: slices.SortFunc for the dual ratio test (zero-alloc, generic)
Follow-up to e9d9386. Microbenchmark (200-elem tie-heavy sort, the ratio test's shape): sort.Slice 6743ns/3allocs, sort.Sort-Interface 3269ns/1alloc, slices.SortFunc 3080ns/0allocs. slices.SortFunc is 2.2x over the original sort.Slice and 6%-faster + zero-alloc over the concrete sort.Interface (whose 1 alloc was interface boxing, x82k+ sorts). Order is byte-identical to sort.Sort — verified across 200 tie-heavy seeds (a throwaway test) and by 020 staying at 774 nodes / 182619 pivots. Drops the concrete sort types. All tests + PuLP green.
1 parent d5c8f4e commit 9aedff3

2 files changed

Lines changed: 20 additions & 21 deletions

File tree

simplex/dual2.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package simplex
33
import (
44
"math"
55
"os"
6-
"sort"
6+
"slices"
77
"time"
88
)
99

@@ -39,14 +39,6 @@ type dual2Cand struct {
3939
ratio float64
4040
}
4141

42-
// dual2CandByRatio: concrete sort.Interface for the DSE ratio test (skips
43-
// sort.Slice reflection; identical pdqsort order).
44-
type dual2CandByRatio []dual2Cand
45-
46-
func (s dual2CandByRatio) Len() int { return len(s) }
47-
func (s dual2CandByRatio) Less(i, j int) bool { return s[i].ratio < s[j].ratio }
48-
func (s dual2CandByRatio) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
49-
5042
// dual2Run is a complete bounded dual simplex modeled on ClpSimplexDual:
5143
// DSE pricing, Harris ratio test with bound flips, run to optimality.
5244
func (lp *LP) dual2Run(st *State) dual2Result {
@@ -246,7 +238,15 @@ func (lp *LP) dual2Run(st *State) dual2Result {
246238
cands = append(cands, dual2Cand{j, dir, at, rd, rd / at})
247239
}
248240
}
249-
sort.Sort(dual2CandByRatio(cands))
241+
slices.SortFunc(cands, func(a, b dual2Cand) int {
242+
switch {
243+
case a.ratio < b.ratio:
244+
return -1
245+
case a.ratio > b.ratio:
246+
return 1
247+
}
248+
return 0
249+
})
250250

251251
// bound-flipping walk, then a Harris window: the largest pivot
252252
// entry inside the first blocker's relaxed ratio wins

simplex/simplex.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package simplex
77
import (
88
"math"
99
"os"
10-
"sort"
10+
"slices"
1111
"time"
1212

1313
"cbcgo/problem"
@@ -689,7 +689,15 @@ func (lp *LP) dualRun(st *State) {
689689
}
690690
}
691691
ws.cands = cands
692-
sort.Sort(dualCandByRatio(cands))
692+
slices.SortFunc(cands, func(a, b dualCand) int {
693+
switch {
694+
case a.ratio < b.ratio:
695+
return -1
696+
case a.ratio > b.ratio:
697+
return 1
698+
}
699+
return 0
700+
})
693701

694702
// dual long step (Clp "dual with flips"): boxed candidates that
695703
// can't absorb the violation get flipped; the overshooter pivots
@@ -804,15 +812,6 @@ type dualCand struct {
804812
ratio float64
805813
}
806814

807-
// dualCandByRatio sorts entering candidates by ascending ratio. Concrete
808-
// sort.Interface (not sort.Slice) so the per-pivot ratio-test sort skips
809-
// reflection — same pdqsort, same comparator, byte-identical order.
810-
type dualCandByRatio []dualCand
811-
812-
func (s dualCandByRatio) Len() int { return len(s) }
813-
func (s dualCandByRatio) Less(i, j int) bool { return s[i].ratio < s[j].ratio }
814-
func (s dualCandByRatio) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
815-
816815
// enterDirs lists the directions a nonbasic variable may enter the basis in.
817816
func enterDirs(s varStat) []float64 {
818817
switch s {

0 commit comments

Comments
 (0)