2022-05-07 18:43:32 +01:00
|
|
|
// Copyright (C) 2022 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package reputation
|
|
|
|
|
|
|
|
import "math"
|
|
|
|
|
|
|
|
// UpdateReputation uses the Beta distribution model to determine a node's reputation.
|
|
|
|
// lambda is the "forgetting factor" which determines how much past info is kept when determining current reputation score.
|
|
|
|
// w is the normalization weight that affects how severely new updates affect the current reputation distribution.
|
|
|
|
func UpdateReputation(isSuccess bool, alpha, beta, lambda, w float64) (newAlpha, newBeta float64) {
|
|
|
|
// v is a single feedback value that allows us to update both alpha and beta
|
|
|
|
var v float64 = -1
|
|
|
|
if isSuccess {
|
|
|
|
v = 1
|
|
|
|
}
|
|
|
|
newAlpha = lambda*alpha + w*(1+v)/2
|
|
|
|
newBeta = lambda*beta + w*(1-v)/2
|
|
|
|
return newAlpha, newBeta
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateReputationMultiple works like UpdateReputation, but applies multiple
|
|
|
|
// successive counts of an event type to the alpha and beta measures.
|
|
|
|
//
|
|
|
|
// With the arguments as named, applies 'count' successful audits. To apply negative
|
|
|
|
// audits, swap the alpha and beta parameters and return values.
|
|
|
|
//
|
|
|
|
// WARNING: GREEK LETTER MATH AHEAD
|
|
|
|
//
|
|
|
|
// Applying n successful audit results to an initial alpha value of α₀ gives a
|
|
|
|
// new α₁ value of:
|
|
|
|
//
|
2022-08-10 16:35:58 +01:00
|
|
|
// α₁ = λⁿα₀ + λⁿ⁻¹w + λⁿ⁻²w + ... + λ²w + λw + w
|
2022-05-07 18:43:32 +01:00
|
|
|
//
|
|
|
|
// The terms with w are the first n terms of a geometric series with coefficient
|
|
|
|
// w and common ratio λ. The closed form formula for the sum of those first n
|
|
|
|
// terms is (w(1-λⁿ) / (1-λ))
|
|
|
|
// (https://en.wikipedia.org/wiki/Geometric_series#Closed-form_formula).
|
|
|
|
// Adding the initial λⁿα₀ term, we get
|
|
|
|
//
|
2022-08-10 16:35:58 +01:00
|
|
|
// α₁ = λⁿα₀ + w(1-λⁿ) / (1-λ)
|
2022-05-07 18:43:32 +01:00
|
|
|
//
|
|
|
|
// The formula has the same structure for beta for n _failures_.
|
|
|
|
//
|
2022-08-10 16:35:58 +01:00
|
|
|
// β₁ = λⁿβ₀ + w(1-λⁿ) / (1-λ)
|
2022-05-07 18:43:32 +01:00
|
|
|
//
|
|
|
|
// For n _failures_,
|
|
|
|
//
|
2022-08-10 16:35:58 +01:00
|
|
|
// α₁ = λⁿα₀
|
2022-05-07 18:43:32 +01:00
|
|
|
//
|
|
|
|
// For n _successes_,
|
|
|
|
//
|
2022-08-10 16:35:58 +01:00
|
|
|
// β₁ = λⁿβ₀
|
2022-05-07 18:43:32 +01:00
|
|
|
func UpdateReputationMultiple(count int, alpha, beta, lambda, w float64) (newAlpha, newBeta float64) {
|
|
|
|
if lambda == 1 {
|
|
|
|
// special case: when the coefficient is 1, the closed-form formula is invalid
|
|
|
|
// (gives NaN because of a division by zero). Fortunately, the replacement
|
|
|
|
// formula in this case is even simpler.
|
|
|
|
newAlpha = alpha + w*float64(count)
|
|
|
|
newBeta = beta
|
|
|
|
} else {
|
|
|
|
lambdaPowN := math.Pow(lambda, float64(count))
|
|
|
|
newAlpha = lambdaPowN*alpha + w*(1-lambdaPowN)/(1-lambda)
|
|
|
|
newBeta = lambdaPowN * beta
|
|
|
|
}
|
|
|
|
return newAlpha, newBeta
|
|
|
|
}
|