feng-shui: move pretty printers to own files, simply sampling
This commit is contained in:
		
							parent
							
								
									0c03b7e6bf
								
							
						
					
					
						commit
						3e4df77541
					
				
							
								
								
									
										10
									
								
								main/errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								main/errors.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| package main | ||||
| 
 | ||||
| type MyError struct { | ||||
| 	Code string | ||||
| 	Msg  string | ||||
| } | ||||
| 
 | ||||
| func (me MyError) Error() string { | ||||
| 	return me.Msg | ||||
| } | ||||
|  | @ -5,11 +5,9 @@ import ( | |||
| 	"errors" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"git.nunosempere.com/NunoSempere/fermi/pretty" | ||||
| 	"git.nunosempere.com/NunoSempere/fermi/sample" | ||||
| 	"math" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
|  | @ -46,11 +44,11 @@ func (p Scalar) Sampler(i int, r sample.State) float64 { | |||
| } | ||||
| 
 | ||||
| func (ln Lognormal) Sampler(i int, r sample.State) float64 { | ||||
| 	return sample.Sample_to(ln.low, ln.high, r) | ||||
| 	return sample.To(ln.low, ln.high, r) | ||||
| } | ||||
| 
 | ||||
| func (beta Beta) Sampler(i int, r sample.State) float64 { | ||||
| 	return sample.Sample_beta(beta.a, beta.b, r) | ||||
| 	return sample.Beta(beta.a, beta.b, r) | ||||
| } | ||||
| 
 | ||||
| func (fs FilledSamples) Sampler(i int, r sample.State) float64 { | ||||
|  | @ -112,94 +110,12 @@ const INIT_DIST Scalar = Scalar(1) | |||
| 
 | ||||
| var N_SAMPLES = 100_000 | ||||
| 
 | ||||
| /* Printers */ | ||||
| func prettyPrintDist(dist Dist) { | ||||
| 	switch v := dist.(type) { | ||||
| 	case Lognormal: | ||||
| 		fmt.Printf("=> ") | ||||
| 		pretty.PrettyPrint2Floats(v.low, v.high) | ||||
| 		fmt.Println() | ||||
| 	case Beta: | ||||
| 		fmt.Printf("=> beta ") | ||||
| 		pretty.PrettyPrint2Floats(v.a, v.b) | ||||
| 		fmt.Println() | ||||
| 	case Scalar: | ||||
| 		fmt.Printf("=> scalar ") | ||||
| 		w := float64(v) | ||||
| 		pretty.PrettyPrintFloat(w) | ||||
| 		fmt.Println() | ||||
| 	case FilledSamples: | ||||
| 		n := len(v.xs) | ||||
| 		sorted_xs := make([]float64, n) | ||||
| 		copy(sorted_xs, v.xs) | ||||
| 		sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 			return sorted_xs[i] < sorted_xs[j] | ||||
| 		}) | ||||
| 
 | ||||
| 		low := sorted_xs[int(math.Round(float64(n)*0.05))] | ||||
| 		high := sorted_xs[int(math.Round(float64(n)*0.95))] | ||||
| 		fmt.Printf("=> ") | ||||
| 		pretty.PrettyPrint2Floats(low, high) | ||||
| 
 | ||||
| 		fmt.Printf(" (") | ||||
| 		pretty.PrettyPrintInt(N_SAMPLES) | ||||
| 		fmt.Printf(" samples)") | ||||
| 		fmt.Println() | ||||
| 	default: | ||||
| 		fmt.Printf("%v\n", v) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func printAndReturnErr(err_msg string) error { | ||||
| 	fmt.Println(err_msg) | ||||
| 	fmt.Println("Type \"help\" (without quotes) to see a pseudogrammar and examples") | ||||
| 	return errors.New(err_msg) | ||||
| } | ||||
| 
 | ||||
| func prettyPrintStats(dist Dist) { | ||||
| 	xs := sample.Sample_serially(dist.Sampler, N_SAMPLES) | ||||
| 	n := len(xs) | ||||
| 
 | ||||
| 	mean := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		mean += xs[i] | ||||
| 	} | ||||
| 	mean /= float64(n) | ||||
| 	fmt.Printf("Mean:   %f\n", mean) | ||||
| 
 | ||||
| 	stdev := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		stdev += math.Pow(xs[i]-mean, 2) | ||||
| 	} | ||||
| 	stdev = math.Sqrt(stdev / float64(n)) | ||||
| 	fmt.Printf("Stdev:  %f\n", stdev) | ||||
| 
 | ||||
| 	sorted_xs := make([]float64, n) | ||||
| 	copy(sorted_xs, xs) | ||||
| 	sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 		return sorted_xs[i] < sorted_xs[j] | ||||
| 	}) | ||||
| 	print_ci := func(ci float64, prefix string) { | ||||
| 		x := sorted_xs[int(math.Round(float64(n)*ci))] | ||||
| 		fmt.Printf("%s%f\n", prefix, x) | ||||
| 	} | ||||
| 	print_ci(0.01, "ci 1%:  ") | ||||
| 	print_ci(0.05, "ci 5%:  ") | ||||
| 	print_ci(0.10, "ci 10%: ") | ||||
| 	print_ci(0.25, "ci 25%: ") | ||||
| 	print_ci(0.50, "ci 50%: ") | ||||
| 	print_ci(0.75, "ci 75%: ") | ||||
| 	print_ci(0.90, "ci 90%: ") | ||||
| 	print_ci(0.95, "ci 95%: ") | ||||
| 	print_ci(0.99, "ci 99%: ") | ||||
| } | ||||
| 
 | ||||
| /* Operations */ | ||||
| // Generic operations with samples
 | ||||
| func operateDistsAsSamples(dist1 Dist, dist2 Dist, op string) (Dist, error) { | ||||
| 
 | ||||
| 	xs := sample.Sample_serially(dist1.Sampler, N_SAMPLES) | ||||
| 	ys := sample.Sample_serially(dist2.Sampler, N_SAMPLES) | ||||
| 	xs := sample.Serially(dist1.Sampler, N_SAMPLES) | ||||
| 	ys := sample.Serially(dist2.Sampler, N_SAMPLES) | ||||
| 	zs := make([]float64, N_SAMPLES) | ||||
| 
 | ||||
| 	for i := 0; i < N_SAMPLES; i++ { | ||||
|  | @ -335,7 +251,7 @@ func operateDists(old_dist Dist, new_dist Dist, op string) (Dist, error) { | |||
| 	case "-": | ||||
| 		return operateDistsAsSamples(old_dist, new_dist, "-") | ||||
| 	default: | ||||
| 		return nil, printAndReturnErr("Can't combine distributions in this way") | ||||
| 		return nil, PrintAndReturnErr("Can't combine distributions in this way") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -344,7 +260,7 @@ func parseMixture(words []string, vars map[string]Dist) (Dist, error) { | |||
| 	// mx, mix, var weight var weight var weight ...
 | ||||
| 	// Check syntax
 | ||||
| 	if len(words)%2 != 0 { | ||||
| 		return nil, printAndReturnErr("Not a mixture. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 		return nil, PrintAndReturnErr("Not a mixture. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 	} | ||||
| 
 | ||||
| 	var fs []func(int, sample.State) float64 | ||||
|  | @ -354,29 +270,29 @@ func parseMixture(words []string, vars map[string]Dist) (Dist, error) { | |||
| 		if i%2 == 0 { | ||||
| 			dist, exists := vars[word] | ||||
| 			if !exists { | ||||
| 				return nil, printAndReturnErr("Expected mixture variable but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 				return nil, PrintAndReturnErr("Expected mixture variable but didn't get a variable. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 			} | ||||
| 			f := dist.Sampler | ||||
| 			fs = append(fs, f) | ||||
| 		} else { | ||||
| 			weight, err := pretty.ParseFloat(word) | ||||
| 			weight, err := ParseFloat(word) | ||||
| 			if err != nil { | ||||
| 				return nil, printAndReturnErr("Expected mixture weight but didn't get a float. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 				return nil, PrintAndReturnErr("Expected mixture weight but didn't get a float. \nMixture syntax: \nmx x 2.5 y 8 z 10\ni.e.: mx var weight var2 weight2 ... var_n weight_n") | ||||
| 			} | ||||
| 			weights = append(weights, weight) | ||||
| 		} | ||||
| 	} | ||||
| 	// Sample from mixture
 | ||||
| 	xs, err := sample.Sample_mixture_serially_from_samplers(fs, weights, N_SAMPLES) | ||||
| 	xs, err := sample.Mixture_serially_from_samplers(fs, weights, N_SAMPLES) | ||||
| 	if err != nil { | ||||
| 		return nil, printAndReturnErr(err.Error()) | ||||
| 		return nil, PrintAndReturnErr(err.Error()) | ||||
| 	} | ||||
| 	return FilledSamples{xs: xs}, nil | ||||
| } | ||||
| 
 | ||||
| /* Parser and repl */ | ||||
| func parseWordsErr(err_msg string) (string, Dist, error) { | ||||
| 	return "", nil, printAndReturnErr(err_msg) | ||||
| 	return "", nil, PrintAndReturnErr(err_msg) | ||||
| } | ||||
| func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist, error) { | ||||
| 
 | ||||
|  | @ -396,7 +312,7 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist | |||
| 		return parseWordsErr("Operator must have operand; can't operate on nothing") | ||||
| 	case 1: | ||||
| 		var_word, var_word_exists := vars[words[0]] | ||||
| 		single_float, err1 := pretty.ParseFloat(words[0]) // abstract this away to search for K/M/B/T/etc.
 | ||||
| 		single_float, err1 := ParseFloat(words[0]) // abstract this away to search for K/M/B/T/etc.
 | ||||
| 		switch { | ||||
| 		case var_word_exists: | ||||
| 			dist = var_word | ||||
|  | @ -406,8 +322,8 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist | |||
| 			return parseWordsErr("Trying to operate on a scalar, but scalar is neither a float nor an assigned variable") | ||||
| 		} | ||||
| 	case 2: | ||||
| 		new_low, err1 := pretty.ParseFloat(words[0]) | ||||
| 		new_high, err2 := pretty.ParseFloat(words[1]) | ||||
| 		new_low, err1 := ParseFloat(words[0]) | ||||
| 		new_high, err2 := ParseFloat(words[1]) | ||||
| 		switch { | ||||
| 		case err1 != nil || err2 != nil: | ||||
| 			return parseWordsErr("Trying to operate by a distribution, but distribution is not specified as two floats") | ||||
|  | @ -422,8 +338,8 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist | |||
| 	case 3: | ||||
| 		switch { | ||||
| 		case words[0] == "beta" || words[0] == "b": | ||||
| 			a, err1 := pretty.ParseFloat(words[1]) | ||||
| 			b, err2 := pretty.ParseFloat(words[2]) | ||||
| 			a, err1 := ParseFloat(words[1]) | ||||
| 			b, err2 := ParseFloat(words[2]) | ||||
| 			if err1 != nil || err2 != nil { | ||||
| 				return parseWordsErr("Trying to specify a beta distribution? Try beta 1 2") | ||||
| 			} | ||||
|  | @ -485,7 +401,7 @@ replForLoop: | |||
| 			stack.old_dist = INIT_DIST | ||||
| 			fmt.Println() | ||||
| 		case words[0] == "stats" || words[0] == "s": | ||||
| 			prettyPrintStats(stack.old_dist) | ||||
| 			PrettyPrintStats(stack.old_dist) | ||||
| 		/* Variable assignment */ | ||||
| 		case words[0] == "=:" && len(words) == 2: | ||||
| 			stack.vars[words[1]] = stack.old_dist | ||||
|  | @ -493,7 +409,7 @@ replForLoop: | |||
| 		case words[0] == "=." && len(words) == 2: | ||||
| 			stack.vars[words[1]] = stack.old_dist | ||||
| 			fmt.Printf("%s ", words[1]) | ||||
| 			prettyPrintDist(stack.old_dist) | ||||
| 			PrettyPrintDist(stack.old_dist) | ||||
| 			stack.old_dist = INIT_DIST | ||||
| 		default: | ||||
| 			op, new_dist, err := parseWordsIntoOpAndDist(words, stack.vars) | ||||
|  | @ -505,7 +421,7 @@ replForLoop: | |||
| 				stack.old_dist = combined_dist | ||||
| 			} | ||||
| 		} | ||||
| 		prettyPrintDist(stack.old_dist) | ||||
| 		PrettyPrintDist(stack.old_dist) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1,9 +1,11 @@ | |||
| package pretty | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"git.nunosempere.com/NunoSempere/fermi/sample" | ||||
| 	"math" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
|  | @ -87,3 +89,85 @@ func ParseFloat(word string) (float64, error) { | |||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* Printers */ | ||||
| func PrettyPrintDist(dist Dist) { | ||||
| 	switch v := dist.(type) { | ||||
| 	case Lognormal: | ||||
| 		fmt.Printf("=> ") | ||||
| 		PrettyPrint2Floats(v.low, v.high) | ||||
| 		fmt.Println() | ||||
| 	case Beta: | ||||
| 		fmt.Printf("=> beta ") | ||||
| 		PrettyPrint2Floats(v.a, v.b) | ||||
| 		fmt.Println() | ||||
| 	case Scalar: | ||||
| 		fmt.Printf("=> scalar ") | ||||
| 		w := float64(v) | ||||
| 		PrettyPrintFloat(w) | ||||
| 		fmt.Println() | ||||
| 	case FilledSamples: | ||||
| 		n := len(v.xs) | ||||
| 		sorted_xs := make([]float64, n) | ||||
| 		copy(sorted_xs, v.xs) | ||||
| 		sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 			return sorted_xs[i] < sorted_xs[j] | ||||
| 		}) | ||||
| 
 | ||||
| 		low := sorted_xs[int(math.Round(float64(n)*0.05))] | ||||
| 		high := sorted_xs[int(math.Round(float64(n)*0.95))] | ||||
| 		fmt.Printf("=> ") | ||||
| 		PrettyPrint2Floats(low, high) | ||||
| 
 | ||||
| 		fmt.Printf(" (") | ||||
| 		PrettyPrintInt(N_SAMPLES) | ||||
| 		fmt.Printf(" samples)") | ||||
| 		fmt.Println() | ||||
| 	default: | ||||
| 		fmt.Printf("%v\n", v) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func PrettyPrintStats(dist Dist) { | ||||
| 	xs := sample.Serially(dist.Sampler, N_SAMPLES) | ||||
| 	n := len(xs) | ||||
| 
 | ||||
| 	mean := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		mean += xs[i] | ||||
| 	} | ||||
| 	mean /= float64(n) | ||||
| 	fmt.Printf("Mean:   %f\n", mean) | ||||
| 
 | ||||
| 	stdev := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		stdev += math.Pow(xs[i]-mean, 2) | ||||
| 	} | ||||
| 	stdev = math.Sqrt(stdev / float64(n)) | ||||
| 	fmt.Printf("Stdev:  %f\n", stdev) | ||||
| 
 | ||||
| 	sorted_xs := make([]float64, n) | ||||
| 	copy(sorted_xs, xs) | ||||
| 	sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 		return sorted_xs[i] < sorted_xs[j] | ||||
| 	}) | ||||
| 	print_ci := func(ci float64, prefix string) { | ||||
| 		x := sorted_xs[int(math.Round(float64(n)*ci))] | ||||
| 		fmt.Printf("%s%f\n", prefix, x) | ||||
| 	} | ||||
| 	print_ci(0.01, "ci 1%:  ") | ||||
| 	print_ci(0.05, "ci 5%:  ") | ||||
| 	print_ci(0.10, "ci 10%: ") | ||||
| 	print_ci(0.25, "ci 25%: ") | ||||
| 	print_ci(0.50, "ci 50%: ") | ||||
| 	print_ci(0.75, "ci 75%: ") | ||||
| 	print_ci(0.90, "ci 90%: ") | ||||
| 	print_ci(0.95, "ci 95%: ") | ||||
| 	print_ci(0.99, "ci 99%: ") | ||||
| } | ||||
| 
 | ||||
| func PrintAndReturnErr(err_msg string) error { | ||||
| 	fmt.Println(err_msg) | ||||
| 	fmt.Println("Type \"help\" (without quotes) to see a pseudogrammar and examples") | ||||
| 	return errors.New(err_msg) | ||||
| } | ||||
							
								
								
									
										2
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								makefile
									
									
									
									
									
								
							|  | @ -1,5 +1,5 @@ | |||
| build: | ||||
| 	go build fermi.go | ||||
| 	go build main/fermi.go main/pretty.go | ||||
| 
 | ||||
| run: | ||||
| 	go run fermi.go | ||||
|  |  | |||
							
								
								
									
										173
									
								
								pretty.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								pretty.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,173 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"git.nunosempere.com/NunoSempere/fermi/sample" | ||||
| 	"math" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| func PrettyPrintInt(n int) { | ||||
| 	switch { | ||||
| 	case math.Abs(float64(n)) >= 1_000_000_000_000: | ||||
| 		fmt.Printf("%.2fT", float64(n)/1_000_000_000_000.0) | ||||
| 	case math.Abs(float64(n)) >= 1_000_000_000: | ||||
| 		fmt.Printf("%.2fB", float64(n)/1_000_000_000.0) | ||||
| 	case math.Abs(float64(n)) >= 1_000_000: | ||||
| 		fmt.Printf("%.2fM", float64(n)/1_000_000.0) | ||||
| 	case math.Abs(float64(n)) >= 1_000: | ||||
| 		fmt.Printf("%.2fK", float64(n)/1_000.0) | ||||
| 	default: | ||||
| 		fmt.Printf("%d", n) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func PrettyPrintFloat(f float64) { | ||||
| 	switch { | ||||
| 	case math.Abs(f) >= 1_000_000_000_000: | ||||
| 		fmt.Printf("%.2fT", f/1_000_000_000_000) | ||||
| 	case math.Abs(f) >= 1_000_000_000: | ||||
| 		fmt.Printf("%.2fB", f/1_000_000_000) | ||||
| 	case math.Abs(f) >= 1_000_000: | ||||
| 		fmt.Printf("%.2fM", f/1_000_000) | ||||
| 	case math.Abs(f) >= 1_000: | ||||
| 		fmt.Printf("%.2fK", f/1_000) | ||||
| 
 | ||||
| 	case math.Abs(f) <= 0.0001: | ||||
| 		fmt.Printf("%.6f", f) | ||||
| 	case math.Abs(f) <= 0.001: | ||||
| 		fmt.Printf("%.5f", f) | ||||
| 	case math.Abs(f) <= 0.01: | ||||
| 		fmt.Printf("%.4f", f) | ||||
| 	case math.Abs(f) <= 0.1: | ||||
| 		fmt.Printf("%.3f", f) | ||||
| 	default: | ||||
| 		fmt.Printf("%.2f", f) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| func PrettyPrint2Floats(low float64, high float64) { | ||||
| 	PrettyPrintFloat(low) | ||||
| 	fmt.Printf(" ") | ||||
| 	PrettyPrintFloat(high) | ||||
| } | ||||
| 
 | ||||
| func multiplyOrPassThroughError(a float64, b float64, err error) (float64, error) { | ||||
| 	if err != nil { | ||||
| 		return b, err | ||||
| 	} else { | ||||
| 		return a * b, nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func ParseFloat(word string) (float64, error) { | ||||
| 	// l = len(word) // assuming no UTF stuff
 | ||||
| 	switch len(word) { | ||||
| 	case 0: | ||||
| 		return 0, errors.New("String to be parsed into float must not be the empty string") | ||||
| 	case 1: | ||||
| 		return strconv.ParseFloat(word, 64) | ||||
| 	} | ||||
| 
 | ||||
| 	n := len(word) - 1 | ||||
| 	f, err := strconv.ParseFloat(word[:n], 64) | ||||
| 	switch word[n] { | ||||
| 	case '%': | ||||
| 		return multiplyOrPassThroughError(0.01, f, err) | ||||
| 	case 'K': | ||||
| 		return multiplyOrPassThroughError(1_000, f, err) | ||||
| 	case 'M': | ||||
| 		return multiplyOrPassThroughError(1_000_000, f, err) | ||||
| 	case 'B': | ||||
| 		return multiplyOrPassThroughError(1_000_000_000, f, err) | ||||
| 	case 'T': | ||||
| 		return multiplyOrPassThroughError(1_000_000_000_000, f, err) | ||||
| 	default: | ||||
| 		return strconv.ParseFloat(word, 64) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* Printers */ | ||||
| func PrettyPrintDist(dist Dist) { | ||||
| 	switch v := dist.(type) { | ||||
| 	case Lognormal: | ||||
| 		fmt.Printf("=> ") | ||||
| 		PrettyPrint2Floats(v.low, v.high) | ||||
| 		fmt.Println() | ||||
| 	case Beta: | ||||
| 		fmt.Printf("=> beta ") | ||||
| 		PrettyPrint2Floats(v.a, v.b) | ||||
| 		fmt.Println() | ||||
| 	case Scalar: | ||||
| 		fmt.Printf("=> scalar ") | ||||
| 		w := float64(v) | ||||
| 		PrettyPrintFloat(w) | ||||
| 		fmt.Println() | ||||
| 	case FilledSamples: | ||||
| 		n := len(v.xs) | ||||
| 		sorted_xs := make([]float64, n) | ||||
| 		copy(sorted_xs, v.xs) | ||||
| 		sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 			return sorted_xs[i] < sorted_xs[j] | ||||
| 		}) | ||||
| 
 | ||||
| 		low := sorted_xs[int(math.Round(float64(n)*0.05))] | ||||
| 		high := sorted_xs[int(math.Round(float64(n)*0.95))] | ||||
| 		fmt.Printf("=> ") | ||||
| 		PrettyPrint2Floats(low, high) | ||||
| 
 | ||||
| 		fmt.Printf(" (") | ||||
| 		PrettyPrintInt(N_SAMPLES) | ||||
| 		fmt.Printf(" samples)") | ||||
| 		fmt.Println() | ||||
| 	default: | ||||
| 		fmt.Printf("%v\n", v) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func PrettyPrintStats(dist Dist) { | ||||
| 	xs := sample.Sample_serially(dist.Sampler, N_SAMPLES) | ||||
| 	n := len(xs) | ||||
| 
 | ||||
| 	mean := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		mean += xs[i] | ||||
| 	} | ||||
| 	mean /= float64(n) | ||||
| 	fmt.Printf("Mean:   %f\n", mean) | ||||
| 
 | ||||
| 	stdev := 0.0 | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		stdev += math.Pow(xs[i]-mean, 2) | ||||
| 	} | ||||
| 	stdev = math.Sqrt(stdev / float64(n)) | ||||
| 	fmt.Printf("Stdev:  %f\n", stdev) | ||||
| 
 | ||||
| 	sorted_xs := make([]float64, n) | ||||
| 	copy(sorted_xs, xs) | ||||
| 	sort.Slice(sorted_xs, func(i, j int) bool { | ||||
| 		return sorted_xs[i] < sorted_xs[j] | ||||
| 	}) | ||||
| 	print_ci := func(ci float64, prefix string) { | ||||
| 		x := sorted_xs[int(math.Round(float64(n)*ci))] | ||||
| 		fmt.Printf("%s%f\n", prefix, x) | ||||
| 	} | ||||
| 	print_ci(0.01, "ci 1%:  ") | ||||
| 	print_ci(0.05, "ci 5%:  ") | ||||
| 	print_ci(0.10, "ci 10%: ") | ||||
| 	print_ci(0.25, "ci 25%: ") | ||||
| 	print_ci(0.50, "ci 50%: ") | ||||
| 	print_ci(0.75, "ci 75%: ") | ||||
| 	print_ci(0.90, "ci 90%: ") | ||||
| 	print_ci(0.95, "ci 95%: ") | ||||
| 	print_ci(0.99, "ci 99%: ") | ||||
| } | ||||
| 
 | ||||
| func PrintAndReturnErr(err_msg string) error { | ||||
| 	fmt.Println(err_msg) | ||||
| 	fmt.Println("Type \"help\" (without quotes) to see a pseudogrammar and examples") | ||||
| 	return errors.New(err_msg) | ||||
| } | ||||
|  | @ -17,49 +17,49 @@ type func64i = func(int, State) float64 | |||
| 
 | ||||
| var global_state = rand.New(rand.NewPCG(uint64(1), uint64(2))) | ||||
| 
 | ||||
| func Sample_int(n int, r State) int { | ||||
| func Int(n int, r State) int { | ||||
| 	return r.IntN(n) | ||||
| } | ||||
| 
 | ||||
| func Sample_unit_uniform(r State) float64 { | ||||
| func Unit_uniform(r State) float64 { | ||||
| 	return r.Float64() | ||||
| } | ||||
| 
 | ||||
| func Sample_unit_normal(r State) float64 { | ||||
| func Unit_normal(r State) float64 { | ||||
| 	return r.NormFloat64() | ||||
| } | ||||
| 
 | ||||
| func Sample_uniform(start float64, end float64, r State) float64 { | ||||
| 	return Sample_unit_uniform(r)*(end-start) + start | ||||
| func Uniform(start float64, end float64, r State) float64 { | ||||
| 	return Unit_uniform(r)*(end-start) + start | ||||
| } | ||||
| 
 | ||||
| func Sample_normal(mean float64, sigma float64, r State) float64 { | ||||
| 	return mean + Sample_unit_normal(r)*sigma | ||||
| func Normal(mean float64, sigma float64, r State) float64 { | ||||
| 	return mean + Unit_normal(r)*sigma | ||||
| } | ||||
| 
 | ||||
| func Sample_lognormal(logmean float64, logstd float64, r State) float64 { | ||||
| 	return (math.Exp(Sample_normal(logmean, logstd, r))) | ||||
| func Lognormal(logmean float64, logstd float64, r State) float64 { | ||||
| 	return (math.Exp(Normal(logmean, logstd, r))) | ||||
| } | ||||
| 
 | ||||
| func Sample_normal_from_90_ci(low float64, high float64, r State) float64 { | ||||
| func Normal_from_90_ci(low float64, high float64, r State) float64 { | ||||
| 	var normal90 float64 = 1.6448536269514727 | ||||
| 	var mean float64 = (high + low) / 2.0 | ||||
| 	var std float64 = (high - low) / (2.0 * normal90) | ||||
| 	return Sample_normal(mean, std, r) | ||||
| 	return Normal(mean, std, r) | ||||
| } | ||||
| 
 | ||||
| func Sample_to(low float64, high float64, r State) float64 { | ||||
| func To(low float64, high float64, r State) float64 { | ||||
| 	// Given a (positive) 90% confidence interval,
 | ||||
| 	// returns a sample from a lognorma with a matching 90% c.i.
 | ||||
| 	// Key idea: If we want a lognormal with 90% confidence interval [a, b]
 | ||||
| 	// we need but get a normal with 90% confidence interval [log(a), log(b)].
 | ||||
| 	// Then see code for Sample_normal_from_90_ci
 | ||||
| 	// Then see code for Normal_from_90_ci
 | ||||
| 	var loglow float64 = math.Log(low) | ||||
| 	var loghigh float64 = math.Log(high) | ||||
| 	return math.Exp(Sample_normal_from_90_ci(loglow, loghigh, r)) | ||||
| 	return math.Exp(Normal_from_90_ci(loglow, loghigh, r)) | ||||
| } | ||||
| 
 | ||||
| func Sample_gamma(alpha float64, r State) float64 { | ||||
| func Gamma(alpha float64, r State) float64 { | ||||
| 
 | ||||
| 	// a simple method for generating gamma variables, marsaglia and wan tsang, 2001
 | ||||
| 	// https://dl.acm.org/doi/pdf/10.1145/358407.358414
 | ||||
|  | @ -80,7 +80,7 @@ func Sample_gamma(alpha float64, r State) float64 { | |||
| 
 | ||||
| 		InnerLoop: | ||||
| 			for { | ||||
| 				x = Sample_unit_normal(r) | ||||
| 				x = Unit_normal(r) | ||||
| 				v = 1.0 + c*x | ||||
| 				if v > 0.0 { | ||||
| 					break InnerLoop | ||||
|  | @ -88,7 +88,7 @@ func Sample_gamma(alpha float64, r State) float64 { | |||
| 			} | ||||
| 
 | ||||
| 			v = v * v * v | ||||
| 			u = Sample_unit_uniform(r) | ||||
| 			u = Unit_uniform(r) | ||||
| 
 | ||||
| 			if u < 1.0-0.0331*(x*x*x*x) { // Condition 1
 | ||||
| 				// the 0.0331 doesn't inspire much confidence
 | ||||
|  | @ -105,17 +105,17 @@ func Sample_gamma(alpha float64, r State) float64 { | |||
| 		} | ||||
| 
 | ||||
| 	} else { | ||||
| 		return Sample_gamma(1.0+alpha, r) * math.Pow(Sample_unit_uniform(r), 1.0/alpha) | ||||
| 		return Gamma(1.0+alpha, r) * math.Pow(Unit_uniform(r), 1.0/alpha) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Sample_beta(a float64, b float64, r State) float64 { | ||||
| 	gamma_a := Sample_gamma(a, r) | ||||
| 	gamma_b := Sample_gamma(b, r) | ||||
| func Beta(a float64, b float64, r State) float64 { | ||||
| 	gamma_a := Gamma(a, r) | ||||
| 	gamma_b := Gamma(b, r) | ||||
| 	return gamma_a / (gamma_a + gamma_b) | ||||
| } | ||||
| 
 | ||||
| func Sample_mixture_once(fs []func64, weights []float64, r State) float64 { | ||||
| func Mixture_once(fs []func64, weights []float64, r State) float64 { | ||||
| 
 | ||||
| 	// fmt.Println("weights initially: ", weights)
 | ||||
| 	var sum_weights float64 = 0 | ||||
|  | @ -149,7 +149,7 @@ func Sample_mixture_once(fs []func64, weights []float64, r State) float64 { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| func Sample_serially(f func64i, n_samples int) []float64 { | ||||
| func Serially(f func64i, n_samples int) []float64 { | ||||
| 	xs := make([]float64, n_samples) | ||||
| 	// var global_state = rand.New(rand.NewPCG(uint64(1), uint64(2)))
 | ||||
| 	for i := 0; i < n_samples; i++ { | ||||
|  | @ -158,7 +158,7 @@ func Sample_serially(f func64i, n_samples int) []float64 { | |||
| 	return xs | ||||
| } | ||||
| 
 | ||||
| func Sample_mixture_serially_from_samples(fs [][]float64, weights []float64, n_samples int) ([]float64, error) { | ||||
| func Mixture_serially_from_samples(fs [][]float64, weights []float64, n_samples int) ([]float64, error) { | ||||
| 
 | ||||
| 	// Checks
 | ||||
| 	if len(weights) != len(fs) { | ||||
|  | @ -206,7 +206,7 @@ func Sample_mixture_serially_from_samples(fs [][]float64, weights []float64, n_s | |||
| 	return xs, nil | ||||
| } | ||||
| 
 | ||||
| func Sample_mixture_serially_from_samplers(fs []func64i, weights []float64, n_samples int) ([]float64, error) { | ||||
| func Mixture_serially_from_samplers(fs []func64i, weights []float64, n_samples int) ([]float64, error) { | ||||
| 
 | ||||
| 	// Checks
 | ||||
| 	if len(weights) != len(fs) { | ||||
|  | @ -247,7 +247,7 @@ func Sample_mixture_serially_from_samplers(fs []func64i, weights []float64, n_sa | |||
| 	return xs, nil | ||||
| } | ||||
| 
 | ||||
| func Sample_parallel(f func64, n_samples int) []float64 { | ||||
| func Parallel(f func64, n_samples int) []float64 { | ||||
| 	var num_threads = 16 | ||||
| 	var xs = make([]float64, n_samples) | ||||
| 	var wg sync.WaitGroup | ||||
|  | @ -283,9 +283,9 @@ func main() { | |||
| 	Sample_many := func(r State) float64 { return Sample_to(2, 10, r) } | ||||
| 	fs := [4](func64){Sample_0, Sample_1, Sample_few, Sample_many} | ||||
| 
 | ||||
| 	model := func(r State) float64 { return Sample_mixture(fs[0:], ws[0:], r) } | ||||
| 	model := func(r State) float64 { return Mixture(fs[0:], ws[0:], r) } | ||||
| 	n_samples := 1_000_000 | ||||
| 	xs := Sample_parallel(model, n_samples) | ||||
| 	xs := Parallel(model, n_samples) | ||||
| 	var avg float64 = 0 | ||||
| 	for _, x := range xs { | ||||
| 		avg += x | ||||
|  | @ -298,7 +298,7 @@ func main() { | |||
| 			var r = rand.New(rand.NewPCG(uint64(1), uint64(2))) | ||||
| 			var avg float64 = 0 | ||||
| 			for i := 0; i < n_samples; i++ { | ||||
| 				avg += Sample_mixture(fs[0:], ws[0:], r) | ||||
| 				avg += Mixture(fs[0:], ws[0:], r) | ||||
| 			} | ||||
| 			avg = avg / float64(n_samples) | ||||
| 			fmt.Printf("Average: %v\n", avg) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user