git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/netmap/aggregator.go (about) 1 package netmap 2 3 import "slices" 4 5 type ( 6 // aggregator can calculate some value across all netmap 7 // such as median, minimum or maximum. 8 aggregator interface { 9 Add(float64) 10 Compute() float64 11 } 12 13 // normalizer normalizes weight. 14 normalizer interface { 15 Normalize(w float64) float64 16 } 17 18 meanAgg struct { 19 mean float64 20 count int 21 } 22 23 minAgg struct { 24 min float64 25 minFound bool 26 } 27 28 meanIQRAgg struct { 29 arr []float64 30 } 31 32 reverseMinNorm struct { 33 min float64 34 } 35 36 sigmoidNorm struct { 37 scale float64 38 } 39 40 // weightFunc calculates n's weight. 41 weightFunc = func(NodeInfo) float64 42 ) 43 44 var ( 45 _ aggregator = (*meanAgg)(nil) 46 _ aggregator = (*minAgg)(nil) 47 _ aggregator = (*meanIQRAgg)(nil) 48 49 _ normalizer = (*reverseMinNorm)(nil) 50 _ normalizer = (*sigmoidNorm)(nil) 51 ) 52 53 // newWeightFunc returns weightFunc which multiplies normalized 54 // capacity and price. 55 func newWeightFunc(capNorm, priceNorm normalizer) weightFunc { 56 return func(n NodeInfo) float64 { 57 return capNorm.Normalize(float64(n.capacity())) * priceNorm.Normalize(float64(n.Price())) 58 } 59 } 60 61 // newMeanAgg returns an aggregator which 62 // computes mean value by recalculating it on 63 // every addition. 64 func newMeanAgg() aggregator { 65 return new(meanAgg) 66 } 67 68 // newMinAgg returns an aggregator which 69 // computes min value. 70 func newMinAgg() aggregator { 71 return new(minAgg) 72 } 73 74 // newReverseMinNorm returns a normalizer which 75 // normalize values in range of 0.0 to 1.0 to a minimum value. 76 func newReverseMinNorm(minV float64) normalizer { 77 return &reverseMinNorm{min: minV} 78 } 79 80 // newSigmoidNorm returns a normalizer which 81 // normalize values in range of 0.0 to 1.0 to a scaled sigmoid. 82 func newSigmoidNorm(scale float64) normalizer { 83 return &sigmoidNorm{scale: scale} 84 } 85 86 func (a *meanAgg) Add(n float64) { 87 c := a.count + 1 88 a.mean = a.mean*(float64(a.count)/float64(c)) + n/float64(c) 89 a.count++ 90 } 91 92 func (a *meanAgg) Compute() float64 { 93 return a.mean 94 } 95 96 func (a *minAgg) Add(n float64) { 97 if !a.minFound { 98 a.min = n 99 a.minFound = true 100 return 101 } 102 103 if n < a.min { 104 a.min = n 105 } 106 } 107 108 func (a *minAgg) Compute() float64 { 109 return a.min 110 } 111 112 func (a *meanIQRAgg) clear() { 113 a.arr = a.arr[:0] 114 } 115 116 func (a *meanIQRAgg) Add(n float64) { 117 a.arr = append(a.arr, n) 118 } 119 120 func (a *meanIQRAgg) Compute() float64 { 121 l := len(a.arr) 122 if l == 0 { 123 return 0 124 } 125 126 slices.Sort(a.arr) 127 128 var minV, maxV float64 129 130 const minLn = 4 131 132 if l < minLn { 133 minV, maxV = a.arr[0], a.arr[l-1] 134 } else { 135 start, end := l/minLn, l*3/minLn-1 136 minV, maxV = a.arr[start], a.arr[end] 137 } 138 139 count := 0 140 sum := float64(0) 141 142 for _, e := range a.arr { 143 if e >= minV && e <= maxV { 144 sum += e 145 count++ 146 } 147 } 148 149 return sum / float64(count) 150 } 151 152 func (r *reverseMinNorm) Normalize(w float64) float64 { 153 return (r.min + 1) / (w + 1) 154 } 155 156 func (r *sigmoidNorm) Normalize(w float64) float64 { 157 if r.scale == 0 { 158 return 0 159 } 160 161 x := w / r.scale 162 163 return x / (1 + x) 164 }