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  }