Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avg vote time calculations #471

Closed
raedah opened this issue May 17, 2018 · 8 comments
Closed

Avg vote time calculations #471

raedah opened this issue May 17, 2018 · 8 comments

Comments

@raedah
Copy link

raedah commented May 17, 2018

Avg vote time calculation being used is currently based on ticket pool param. On mainnet 8192 blocks is what is used and arrives at 28.44 days, but average vote time probability calculations comes to 8102 blocks which is 28.13 days. Needs to be resolved on home page ROI calculation as well as the APR pull request. #468

This can also be worked into an improved version of the luck calculation, probably as a separate PR.

@davecgh
Copy link
Member

davecgh commented May 18, 2018

For reference, that should be 7860, not 8102. There was a typo in the formula.

Since the probability of voting by a given block formula is:

image

The EV is:

~7860 per Wolfram.

This is confirmed by calculating the statistics from the chain across all tickets since the beginning with dcrvotetimes:

Calculating average vote time through block height 239700...
Height..1000..2000..3000..4000..5000..6000..7000..8000..9000
...
..230000..231000..232000..233000..234000..235000..236000..237000..238000..239000..done
Mean wait for 1156623 votes: 7859.5 blocks, 27.23 days

@papacarp
Copy link
Member

papacarp commented Jun 5, 2018

@davecgh any recommendations on how to code this into GO so we can derive from params rather than hard code 7860? I tried a for loop sum, but end up with overflows rather quickly. Looked briefly for internet references to go and summations - NL.

@davecgh
Copy link
Member

davecgh commented Jun 5, 2018

There are a lot of techniques you can use to the estimate the result, but rather than going heavy into the math, I would suggest just using wolfram to calculate the values for the two networks, since they aren't going to change very often, if ever, and then add an init function which asserts the relevant params have not changed. e.g.

func assertProbabilityParams(params *chaincfg.Params, ticketPoolSize, ticketsPerBlock uint16, ticketExpiry uint32) {
	if params.TicketPoolSize != ticketPoolSize {
		panic(fmt.Sprintf("ticket pool size for %s is %d instead of expected %d",
			params.Name, params.TicketPoolSize, ticketPoolSize)
	}
	// Same for the others
}

func init() {
	assertProbabilityParams(&chaincfg.MainNetParams, 8192, 5, 40960)
	assertProbabilityParams(&chaincfg.TestNet2Params, 1024, 5, 6144)
}

@chappjc
Copy link
Member

chappjc commented Jun 5, 2018

It's not too bad if you apply a log to the equation:

E = sum(x p(x))
p(x) = a^(x-1) / b^x

fn(x) = log(x p(x)) = log(x) + log(p(x))
                    = log(x) + (x-1)*log(a) - x*log(b)

a = 8191, b = 8192
E = sum(exp(fn(x)), x=1 to 40960)

7860.917491203549

@chappjc
Copy link
Member

chappjc commented Jun 5, 2018

Playground

Prints 7860.917491184401

@davecgh
Copy link
Member

davecgh commented Jun 6, 2018

I still suspect that asserting things haven't changed and just using constants for the values along with comments how they were derived is probably a good idea since there are certainly a lot of other assumptions made accordingly, but it can certainly be estimated via logs as @chappjc pointed out.

If you want to go that route, you can simplify it further by knowing that the target pool size is always the number of tickets per block times the defined ticket pool size. The result is that the CDF will reduce to sum((poolSize - 1)^(B-1) / poolSize^B) and consequently the code to calculate it via logs would be:

package main

import (
	"fmt"
	"math"

	"github.com/decred/dcrd/chaincfg"
)

func calcMeanVotingBlocks(params *chaincfg.Params) int64 {
	poolSize := float64(params.TicketPoolSize)
	var v float64
	for i := float64(1); i <= float64(params.TicketExpiry); i++ {
		v += math.Exp(math.Log(i) + (i-1)*math.Log(poolSize-1) - i*math.Log(poolSize))
	}
	return int64(v)
}

func main() {
	fmt.Println("mainnet", calcMeanVotingBlocks(&chaincfg.MainNetParams))
	fmt.Println("testnet2", calcMeanVotingBlocks(&chaincfg.TestNet2Params))
}

Output:

mainnet: 7860
testnet2: 1006

EDIT: You can, of course, make that more efficient by computing the constant math.Log(poolSize-1) and math.Log(poolSize) outside of the loop.

@papacarp
Copy link
Member

papacarp commented Jun 6, 2018

Thank you. This is exactly what I needed. I spoke with @raedah and we are going to put in a variation of the calculated version you both came up with.

@chappjc
Copy link
Member

chappjc commented Jun 30, 2018

resolved by PR #468, specifically explorer.calcMeanVotingBlocks

@chappjc chappjc closed this as completed Jun 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants