Merge pull request #3155 from filecoin-project/misc/selection-misc
Use conditional probability for miners
This commit is contained in:
commit
1dd554ad71
@ -26,6 +26,28 @@ func noWinnersProb() []float64 {
|
|||||||
return noWinnersProbCache
|
return noWinnersProbCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var noWinnersProbAssumingCache []float64
|
||||||
|
var noWinnersProbAssumingOnce sync.Once
|
||||||
|
|
||||||
|
func noWinnersProbAssumingMoreThanOne() []float64 {
|
||||||
|
noWinnersProbAssumingOnce.Do(func() {
|
||||||
|
cond := math.Log(-1 + math.Exp(5))
|
||||||
|
poissPdf := func(x float64) float64 {
|
||||||
|
const Mu = 5
|
||||||
|
lg, _ := math.Lgamma(x + 1)
|
||||||
|
result := math.Exp((math.Log(Mu) * x) - lg - cond)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]float64, 0, MaxBlocks)
|
||||||
|
for i := 0; i < MaxBlocks; i++ {
|
||||||
|
out = append(out, poissPdf(float64(i+1)))
|
||||||
|
}
|
||||||
|
noWinnersProbAssumingCache = out
|
||||||
|
})
|
||||||
|
return noWinnersProbAssumingCache
|
||||||
|
}
|
||||||
|
|
||||||
func binomialCoefficient(n, k float64) float64 {
|
func binomialCoefficient(n, k float64) float64 {
|
||||||
if k > n {
|
if k > n {
|
||||||
return math.NaN()
|
return math.NaN()
|
||||||
@ -40,7 +62,7 @@ func binomialCoefficient(n, k float64) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mp *MessagePool) blockProbabilities(tq float64) []float64 {
|
func (mp *MessagePool) blockProbabilities(tq float64) []float64 {
|
||||||
noWinners := noWinnersProb() // cache this
|
noWinners := noWinnersProbAssumingMoreThanOne()
|
||||||
|
|
||||||
p := 1 - tq
|
p := 1 - tq
|
||||||
binoPdf := func(x, trials float64) float64 {
|
binoPdf := func(x, trials float64) float64 {
|
||||||
@ -72,7 +94,7 @@ func (mp *MessagePool) blockProbabilities(tq float64) []float64 {
|
|||||||
for place := 0; place < MaxBlocks; place++ {
|
for place := 0; place < MaxBlocks; place++ {
|
||||||
var pPlace float64
|
var pPlace float64
|
||||||
for otherWinners, pCase := range noWinners {
|
for otherWinners, pCase := range noWinners {
|
||||||
pPlace += pCase * binoPdf(float64(place), float64(otherWinners+1))
|
pPlace += pCase * binoPdf(float64(place), float64(otherWinners))
|
||||||
}
|
}
|
||||||
out = append(out, pPlace)
|
out = append(out, pPlace)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package messagepool
|
package messagepool
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func TestBlockProbability(t *testing.T) {
|
func TestBlockProbability(t *testing.T) {
|
||||||
mp := &MessagePool{}
|
mp := &MessagePool{}
|
||||||
@ -13,3 +18,26 @@ func TestBlockProbability(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWinnerProba(t *testing.T) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
const N = 1000000
|
||||||
|
winnerProba := noWinnersProb()
|
||||||
|
sum := 0
|
||||||
|
for i := 0; i < N; i++ {
|
||||||
|
minersRand := rand.Float64()
|
||||||
|
j := 0
|
||||||
|
for ; j < MaxBlocks; j++ {
|
||||||
|
minersRand -= winnerProba[j]
|
||||||
|
if minersRand < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum += j
|
||||||
|
}
|
||||||
|
|
||||||
|
if avg := float64(sum) / N; math.Abs(avg-5) > 0.01 {
|
||||||
|
t.Fatalf("avg too far off: %f", avg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -834,7 +834,7 @@ func TestOptimalMessageSelection3(t *testing.T) {
|
|||||||
nMessages := int(build.BlockGasLimit/gasLimit) + 1
|
nMessages := int(build.BlockGasLimit/gasLimit) + 1
|
||||||
for i := 0; i < nMessages; i++ {
|
for i := 0; i < nMessages; i++ {
|
||||||
for j := 0; j < nActors; j++ {
|
for j := 0; j < nActors; j++ {
|
||||||
premium := 500000 + 20000*(nActors-j) + (nMessages+2-i)/(3*nActors) + i%3
|
premium := 500000 + 10000*(nActors-j) + (nMessages+2-i)/(30*nActors) + i%3
|
||||||
m := makeTestMessage(wallets[j], actors[j], actors[j%nActors], uint64(i), gasLimit, uint64(premium))
|
m := makeTestMessage(wallets[j], actors[j], actors[j%nActors], uint64(i), gasLimit, uint64(premium))
|
||||||
mustAdd(t, mp, m)
|
mustAdd(t, mp, m)
|
||||||
}
|
}
|
||||||
@ -874,7 +874,7 @@ func TestOptimalMessageSelection3(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium func() uint64) (float64, float64) {
|
func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium func() uint64) (float64, float64, float64) {
|
||||||
// in this test we use 300 actors and send 10 blocks of messages.
|
// in this test we use 300 actors and send 10 blocks of messages.
|
||||||
// actors send with an randomly distributed premium dictated by the getPremium function.
|
// actors send with an randomly distributed premium dictated by the getPremium function.
|
||||||
// a number of miners select with varying ticket quality and we compare the
|
// a number of miners select with varying ticket quality and we compare the
|
||||||
@ -934,8 +934,11 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
capacityBoost := 0.0
|
totalGreedyCapacity := 0.0
|
||||||
rewardBoost := 0.0
|
totalGreedyReward := 0.0
|
||||||
|
totalOptimalCapacity := 0.0
|
||||||
|
totalOptimalReward := 0.0
|
||||||
|
totalBestTQReward := 0.0
|
||||||
const runs = 1
|
const runs = 1
|
||||||
for i := 0; i < runs; i++ {
|
for i := 0; i < runs; i++ {
|
||||||
// 2. optimal selection
|
// 2. optimal selection
|
||||||
@ -945,31 +948,38 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
|
|||||||
for ; i < MaxBlocks && minersRand > 0; i++ {
|
for ; i < MaxBlocks && minersRand > 0; i++ {
|
||||||
minersRand -= winerProba[i]
|
minersRand -= winerProba[i]
|
||||||
}
|
}
|
||||||
nMiners := i
|
nMiners := i - 1
|
||||||
if nMiners == 0 {
|
if nMiners < 1 {
|
||||||
nMiners = 1
|
nMiners = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
optMsgs := make(map[cid.Cid]*types.SignedMessage)
|
optMsgs := make(map[cid.Cid]*types.SignedMessage)
|
||||||
|
bestTq := 0.0
|
||||||
|
var bestMsgs []*types.SignedMessage
|
||||||
for j := 0; j < nMiners; j++ {
|
for j := 0; j < nMiners; j++ {
|
||||||
tq := rng.Float64()
|
tq := rng.Float64()
|
||||||
msgs, err := mp.SelectMessages(ts, tq)
|
msgs, err := mp.SelectMessages(ts, tq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if tq > bestTq {
|
||||||
|
bestMsgs = msgs
|
||||||
|
}
|
||||||
|
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
optMsgs[m.Cid()] = m
|
optMsgs[m.Cid()] = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalGreedyCapacity += float64(len(greedyMsgs))
|
||||||
|
totalOptimalCapacity += float64(len(optMsgs))
|
||||||
boost := float64(len(optMsgs)) / float64(len(greedyMsgs))
|
boost := float64(len(optMsgs)) / float64(len(greedyMsgs))
|
||||||
capacityBoost += boost
|
|
||||||
|
|
||||||
t.Logf("nMiners: %d", nMiners)
|
t.Logf("nMiners: %d", nMiners)
|
||||||
t.Logf("greedy capacity %d, optimal capacity %d (x %.1f )", len(greedyMsgs),
|
t.Logf("greedy capacity %d, optimal capacity %d (x %.1f )", len(greedyMsgs),
|
||||||
len(optMsgs), boost)
|
len(optMsgs), boost)
|
||||||
if len(greedyMsgs) > len(optMsgs) {
|
if len(greedyMsgs) > len(optMsgs) {
|
||||||
t.Fatal("greedy capacity higher than optimal capacity; wtf")
|
t.Errorf("greedy capacity higher than optimal capacity; wtf")
|
||||||
}
|
}
|
||||||
|
|
||||||
greedyReward := big.NewInt(0)
|
greedyReward := big.NewInt(0)
|
||||||
@ -982,25 +992,34 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
|
|||||||
optReward.Add(optReward, mp.getGasReward(m, baseFee, ts))
|
optReward.Add(optReward, mp.getGasReward(m, baseFee, ts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bestTqReward := big.NewInt(0)
|
||||||
|
for _, m := range bestMsgs {
|
||||||
|
bestTqReward.Add(bestTqReward, mp.getGasReward(m, baseFee, ts))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBestTQReward += float64(bestTqReward.Uint64())
|
||||||
|
|
||||||
nMinersBig := big.NewInt(int64(nMiners))
|
nMinersBig := big.NewInt(int64(nMiners))
|
||||||
greedyAvgReward, _ := new(big.Rat).SetFrac(greedyReward, nMinersBig).Float64()
|
greedyAvgReward, _ := new(big.Rat).SetFrac(greedyReward, nMinersBig).Float64()
|
||||||
|
totalGreedyReward += greedyAvgReward
|
||||||
optimalAvgReward, _ := new(big.Rat).SetFrac(optReward, nMinersBig).Float64()
|
optimalAvgReward, _ := new(big.Rat).SetFrac(optReward, nMinersBig).Float64()
|
||||||
|
totalOptimalReward += optimalAvgReward
|
||||||
|
|
||||||
boost = optimalAvgReward / greedyAvgReward
|
boost = optimalAvgReward / greedyAvgReward
|
||||||
rewardBoost += boost
|
|
||||||
t.Logf("greedy reward: %.0f, optimal reward: %.0f (x %.1f )", greedyAvgReward,
|
t.Logf("greedy reward: %.0f, optimal reward: %.0f (x %.1f )", greedyAvgReward,
|
||||||
optimalAvgReward, boost)
|
optimalAvgReward, boost)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
capacityBoost /= runs
|
capacityBoost := totalOptimalCapacity / totalGreedyCapacity
|
||||||
rewardBoost /= runs
|
rewardBoost := totalOptimalReward / totalGreedyReward
|
||||||
t.Logf("Average capacity boost: %f", capacityBoost)
|
t.Logf("Average capacity boost: %f", capacityBoost)
|
||||||
t.Logf("Average reward boost: %f", rewardBoost)
|
t.Logf("Average reward boost: %f", rewardBoost)
|
||||||
|
t.Logf("Average best tq reward: %f", totalBestTQReward/runs/1e12)
|
||||||
|
|
||||||
logging.SetLogLevel("messagepool", "info")
|
logging.SetLogLevel("messagepool", "info")
|
||||||
|
|
||||||
return capacityBoost, rewardBoost
|
return capacityBoost, rewardBoost, totalBestTQReward / runs / 1e12
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeExpPremiumDistribution(rng *rand.Rand) func() uint64 {
|
func makeExpPremiumDistribution(rng *rand.Rand) func() uint64 {
|
||||||
@ -1018,35 +1037,41 @@ func makeZipfPremiumDistribution(rng *rand.Rand) func() uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCompetitiveMessageSelectionExp(t *testing.T) {
|
func TestCompetitiveMessageSelectionExp(t *testing.T) {
|
||||||
var capacityBoost, rewardBoost float64
|
var capacityBoost, rewardBoost, tqReward float64
|
||||||
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
||||||
for _, seed := range seeds {
|
for _, seed := range seeds {
|
||||||
t.Log("running competitive message selection with Exponential premium distribution and seed", seed)
|
t.Log("running competitive message selection with Exponential premium distribution and seed", seed)
|
||||||
rng := rand.New(rand.NewSource(seed))
|
rng := rand.New(rand.NewSource(seed))
|
||||||
cb, rb := testCompetitiveMessageSelection(t, rng, makeExpPremiumDistribution(rng))
|
cb, rb, tqR := testCompetitiveMessageSelection(t, rng, makeExpPremiumDistribution(rng))
|
||||||
capacityBoost += cb
|
capacityBoost += cb
|
||||||
rewardBoost += rb
|
rewardBoost += rb
|
||||||
|
tqReward += tqR
|
||||||
}
|
}
|
||||||
|
|
||||||
capacityBoost /= float64(len(seeds))
|
capacityBoost /= float64(len(seeds))
|
||||||
rewardBoost /= float64(len(seeds))
|
rewardBoost /= float64(len(seeds))
|
||||||
|
tqReward /= float64(len(seeds))
|
||||||
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
||||||
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
||||||
|
t.Logf("Average reward of best ticket across all seeds: %f", tqReward)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompetitiveMessageSelectionZipf(t *testing.T) {
|
func TestCompetitiveMessageSelectionZipf(t *testing.T) {
|
||||||
var capacityBoost, rewardBoost float64
|
var capacityBoost, rewardBoost, tqReward float64
|
||||||
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
seeds := []int64{1947, 1976, 2020, 2100, 10000, 143324, 432432, 131, 32, 45}
|
||||||
for _, seed := range seeds {
|
for _, seed := range seeds {
|
||||||
t.Log("running competitive message selection with Zipf premium distribution and seed", seed)
|
t.Log("running competitive message selection with Zipf premium distribution and seed", seed)
|
||||||
rng := rand.New(rand.NewSource(seed))
|
rng := rand.New(rand.NewSource(seed))
|
||||||
cb, rb := testCompetitiveMessageSelection(t, rng, makeZipfPremiumDistribution(rng))
|
cb, rb, tqR := testCompetitiveMessageSelection(t, rng, makeZipfPremiumDistribution(rng))
|
||||||
capacityBoost += cb
|
capacityBoost += cb
|
||||||
rewardBoost += rb
|
rewardBoost += rb
|
||||||
|
tqReward += tqR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tqReward /= float64(len(seeds))
|
||||||
capacityBoost /= float64(len(seeds))
|
capacityBoost /= float64(len(seeds))
|
||||||
rewardBoost /= float64(len(seeds))
|
rewardBoost /= float64(len(seeds))
|
||||||
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
t.Logf("Average capacity boost across all seeds: %f", capacityBoost)
|
||||||
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
t.Logf("Average reward boost across all seeds: %f", rewardBoost)
|
||||||
|
t.Logf("Average reward of best ticket across all seeds: %f", tqReward)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user