pkg/trimaran/lowriskovercommitment/beta.go (126 lines of code) (raw):

/* Copyright 2023 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package lowriskovercommitment import ( "bytes" "fmt" "math" gonum "gonum.org/v1/gonum/mathext" ) // BetaDistribution : // // http://en.wikipedia.org/wiki/Beta_distribution // alpha shape parameter (alpha > 0) // beta shape parameter (beta > 0) type BetaDistribution struct { alpha float64 beta float64 isValid bool firstMoment float64 secondMoment float64 thirdMoment float64 } // NewBetaDistribution : constructor func NewBetaDistribution(alpha, beta float64) *BetaDistribution { b := new(BetaDistribution) b.isValid = checkValidity(alpha, beta) if !b.isValid { return nil } b.alpha = alpha b.beta = beta // set moments b.computeMoments() return b } // checkValidity : check validity of parameters func checkValidity(alpha, beta float64) bool { return alpha > 0 && beta > 0 } // computeMoments : compute the first three moments of the distribution func (b *BetaDistribution) computeMoments() bool { if !b.isValid { return false } b.firstMoment = b.alpha / (b.alpha + b.beta) b.secondMoment = b.firstMoment * (b.alpha + 1) / (b.alpha + b.beta + 1) b.thirdMoment = b.secondMoment * (b.alpha + 2) / (b.alpha + b.beta + 2) return true } // Mean : E[X], the mean of the random variable X. func (b *BetaDistribution) Mean() float64 { return b.firstMoment } // Variance : // V[X], the variance of the random variable X. // V[X] = E[X^2] - (E[X])^2 func (b *BetaDistribution) Variance() float64 { return b.secondMoment - b.firstMoment*b.firstMoment } // DistributionFunction : // Probability distribution function, PDF(x) of random variable X. // The probability that X <= x. func (b *BetaDistribution) DistributionFunction(x float64) float64 { p := RegularizedIncomplete(x, b.alpha, b.beta) if math.IsNaN(p) || p < 0 || p > 1 { p = 0 } return p } // DensityFunction : // Probability density function, pdf(x) of random variable X. // The probability that x < X < x + dx. func (b *BetaDistribution) DensityFunction(x float64) float64 { var betax = math.Pow(x, b.alpha-1.) * math.Pow((1.-x), (b.beta-1.)) var betac = Complete(b.alpha, b.beta) var pdf = betax / betac if math.IsNaN(pdf) || pdf < 0 { pdf = 0 } return pdf } // MatchMoments : Match the first two moments: m1 and m2 func (b *BetaDistribution) MatchMoments(m1, m2 float64) bool { variance := m2 - m1*m1 if m1 < 0 || m1 > 1 || variance < 0 || variance >= m1*(1-m1) { return false } temp := (m1 * (1 - m1) / variance) - 1 temp = math.Max(temp, math.SmallestNonzeroFloat64) b.alpha = m1 * temp b.beta = (1 - m1) * temp return b.computeMoments() } // GetMaxVariance : Maximum variance for a given mean func GetMaxVariance(m1 float64) float64 { if m1 > 0 && m1 < 1 { return m1 * (1 - m1) } return 0 } // GetAlpha : the alpha parameter func (b *BetaDistribution) GetAlpha() float64 { return b.alpha } // GetBeta : the beta parameter func (b *BetaDistribution) GetBeta() float64 { return b.beta } // Print : toString() of the distribution func (b *BetaDistribution) Print() string { var buf bytes.Buffer fmt.Fprintf(&buf, "BetaDistribution: ") fmt.Fprintf(&buf, "alpha = %f; beta = %f; ", b.alpha, b.beta) fmt.Fprintf(&buf, "mean = %f; var = %f; ", b.Mean(), b.Variance()) fmt.Fprintf(&buf, "m1 = %f; m2 = %f; m3 = %f; ", b.firstMoment, b.secondMoment, b.thirdMoment) return buf.String() } /* * Beta related functions */ // Complete : complete beta function, B(a,b), a,b > 0 func Complete(a, b float64) float64 { return gonum.Beta(a, b) } // RegularizedIncomplete : I_x(a,b) = B(x;a,b) / B(a,b), a,b > 0, x in [0,1], // where B(x;a,b) is the incomplete and B(a,b) is the complete beta function func RegularizedIncomplete(x, a, b float64) float64 { if a <= 0 || b <= 0 || x < 0 || x > 1 { return math.NaN() } switch x { case 0: return 0 case 1: return 1 default: return gonum.RegIncBeta(a, b, x) } } // ComputeProbability : The probability that the resource utilization is less than or equal to a given threshold value func ComputeProbability(mu, sigma, threshold float64) (float64, *BetaDistribution) { if mu == 0 || (sigma == 0 && mu <= threshold) { return 1, nil } if sigma == 0 && mu > threshold { return 0, nil } m1 := mu m2 := (sigma * sigma) + (mu * mu) betaDist := NewBetaDistribution(1, 1) if !betaDist.MatchMoments(m1, m2) { return 0, nil } belowLimit := betaDist.DistributionFunction(threshold) if math.IsNaN(belowLimit) { return 1, betaDist } return belowLimit, betaDist }