logarithmic_mapping.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Unless explicitly stated otherwise all files in this repository are licensed
  2. // under the Apache License 2.0.
  3. // This product includes software developed at Datadog (https://www.datadoghq.com/).
  4. // Copyright 2021 Datadog, Inc.
  5. package mapping
  6. import (
  7. "bytes"
  8. "errors"
  9. "fmt"
  10. "math"
  11. enc "github.com/DataDog/sketches-go/ddsketch/encoding"
  12. "github.com/DataDog/sketches-go/ddsketch/pb/sketchpb"
  13. )
  14. // An IndexMapping that is memory-optimal, that is to say that given a targeted relative accuracy, it
  15. // requires the least number of indices to cover a given range of values. This is done by logarithmically
  16. // mapping floating-point values to integers.
  17. type LogarithmicMapping struct {
  18. relativeAccuracy float64
  19. multiplier float64
  20. normalizedIndexOffset float64
  21. }
  22. func NewLogarithmicMapping(relativeAccuracy float64) (*LogarithmicMapping, error) {
  23. if relativeAccuracy <= 0 || relativeAccuracy >= 1 {
  24. return nil, errors.New("The relative accuracy must be between 0 and 1.")
  25. }
  26. m := &LogarithmicMapping{
  27. relativeAccuracy: relativeAccuracy,
  28. multiplier: 1 / math.Log1p(2*relativeAccuracy/(1-relativeAccuracy)),
  29. }
  30. return m, nil
  31. }
  32. func NewLogarithmicMappingWithGamma(gamma, indexOffset float64) (*LogarithmicMapping, error) {
  33. if gamma <= 1 {
  34. return nil, errors.New("Gamma must be greater than 1.")
  35. }
  36. m := &LogarithmicMapping{
  37. relativeAccuracy: 1 - 2/(1+gamma),
  38. multiplier: 1 / math.Log(gamma),
  39. normalizedIndexOffset: indexOffset,
  40. }
  41. return m, nil
  42. }
  43. func (m *LogarithmicMapping) Equals(other IndexMapping) bool {
  44. o, ok := other.(*LogarithmicMapping)
  45. if !ok {
  46. return false
  47. }
  48. tol := 1e-12
  49. return (withinTolerance(m.multiplier, o.multiplier, tol) && withinTolerance(m.normalizedIndexOffset, o.normalizedIndexOffset, tol))
  50. }
  51. func (m *LogarithmicMapping) Index(value float64) int {
  52. index := math.Log(value)*m.multiplier + m.normalizedIndexOffset
  53. if index >= 0 {
  54. return int(index)
  55. } else {
  56. return int(index) - 1 // faster than Math.Floor
  57. }
  58. }
  59. func (m *LogarithmicMapping) Value(index int) float64 {
  60. return m.LowerBound(index) * (1 + m.relativeAccuracy)
  61. }
  62. func (m *LogarithmicMapping) LowerBound(index int) float64 {
  63. return math.Exp((float64(index) - m.normalizedIndexOffset) / m.multiplier)
  64. }
  65. func (m *LogarithmicMapping) MinIndexableValue() float64 {
  66. return math.Max(
  67. math.Exp((math.MinInt32-m.normalizedIndexOffset)/m.multiplier+1), // so that index >= MinInt32
  68. minNormalFloat64*(1+m.relativeAccuracy)/(1-m.relativeAccuracy),
  69. )
  70. }
  71. func (m *LogarithmicMapping) MaxIndexableValue() float64 {
  72. return math.Min(
  73. math.Exp((math.MaxInt32-m.normalizedIndexOffset)/m.multiplier-1), // so that index <= MaxInt32
  74. math.Exp(expOverflow)/(1+m.relativeAccuracy), // so that math.Exp does not overflow
  75. )
  76. }
  77. func (m *LogarithmicMapping) RelativeAccuracy() float64 {
  78. return m.relativeAccuracy
  79. }
  80. func (m *LogarithmicMapping) gamma() float64 {
  81. return (1 + m.relativeAccuracy) / (1 - m.relativeAccuracy)
  82. }
  83. // Generates a protobuf representation of this LogarithicMapping.
  84. func (m *LogarithmicMapping) ToProto() *sketchpb.IndexMapping {
  85. return &sketchpb.IndexMapping{
  86. Gamma: m.gamma(),
  87. IndexOffset: m.normalizedIndexOffset,
  88. Interpolation: sketchpb.IndexMapping_NONE,
  89. }
  90. }
  91. func (m *LogarithmicMapping) Encode(b *[]byte) {
  92. enc.EncodeFlag(b, enc.FlagIndexMappingBaseLogarithmic)
  93. enc.EncodeFloat64LE(b, m.gamma())
  94. enc.EncodeFloat64LE(b, m.normalizedIndexOffset)
  95. }
  96. func (m *LogarithmicMapping) string() string {
  97. var buffer bytes.Buffer
  98. buffer.WriteString(fmt.Sprintf("relativeAccuracy: %v, multiplier: %v, normalizedIndexOffset: %v\n", m.relativeAccuracy, m.multiplier, m.normalizedIndexOffset))
  99. return buffer.String()
  100. }
  101. var _ IndexMapping = (*LogarithmicMapping)(nil)