fermi

A minimalist calculator for estimating with distributions
Log | Files | Refs | README

commit 239e21509fddc21f28af1d26182099cdc204f296
parent 31339383342de14bcc5030acd3b11439c5d10938
Author: NunoSempere <nuno.semperelh@protonmail.com>
Date:   Tue, 18 Jun 2024 22:44:24 -0400

fengshui

Diffstat:
A.fermi.go.swp | 0
Mfermi.go | 179++++++++++++++++++++++++++++++-------------------------------------------------
Apretty/pretty.go | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 120 insertions(+), 111 deletions(-)

diff --git a/.fermi.go.swp b/.fermi.go.swp Binary files differ. diff --git a/fermi.go b/fermi.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "git.nunosempere.com/NunoSempere/fermi/sample" + "git.nunosempere.com/NunoSempere/fermi/pretty" "math" "os" "sort" @@ -39,6 +40,33 @@ type FilledSamples struct { xs []float64 } +/* Dist interface functions */ +// https://go.dev/tour/methods/9 +func (p Scalar) Samples() []float64 { + xs := make([]float64, N_SAMPLES) + for i := 0; i < N_SAMPLES; i++ { + xs[i] = float64(p) + } + return xs +} + +func (ln Lognormal) Samples() []float64 { + sampler := func(r sample.Src) float64 { return sample.Sample_to(ln.low, ln.high, r) } + // return sample.Sample_parallel(sampler, N_SAMPLES) + // Can't do parallel because then I'd have to await throughout the code + return sample.Sample_serially(sampler, N_SAMPLES) +} + +func (beta Beta) Samples() []float64 { + sampler := func(r sample.Src) float64 { return sample.Sample_beta(beta.a, beta.b, r) } + // return sample.Sample_parallel(sampler, N_SAMPLES) + return sample.Sample_serially(sampler, N_SAMPLES) +} + +func (fs FilledSamples) Samples() []float64 { + return fs.xs +} + /* Constants */ const GENERAL_ERR_MSG = " Operation | Variable assignment | Special\n" + " Operation: operator operand\n" + @@ -66,35 +94,44 @@ const NORMAL90CONFIDENCE = 1.6448536269514727 const INIT_DIST Scalar = Scalar(1) const N_SAMPLES = 100_000 -/* Dist interface functions */ -// https://go.dev/tour/methods/9 -func (p Scalar) Samples() []float64 { - xs := make([]float64, N_SAMPLES) - for i := 0; i < N_SAMPLES; i++ { - xs[i] = float64(p) +/* Pretty print for distributions */ +// Needs types +func prettyPrintDist(dist Dist) { + switch v := dist.(type) { + case Lognormal: + fmt.Printf("=> ") + pretty.PrettyPrint2Floats(v.low, v.high) + case FilledSamples: + tmp_xs := make([]float64, N_SAMPLES) + copy(tmp_xs, v.xs) + sort.Slice(tmp_xs, func(i, j int) bool { + return tmp_xs[i] < tmp_xs[j] + }) + low_int := N_SAMPLES / 20 + low := tmp_xs[low_int] + high_int := N_SAMPLES * 19 / 20 + high := tmp_xs[high_int] + fmt.Printf("=> ") + pretty.PrettyPrintFloat(low) + fmt.Printf(" ") + pretty.PrettyPrintFloat(high) + fmt.Printf(" (") + pretty.PrettyPrintInt(N_SAMPLES) + fmt.Printf(" samples)\n") + case Beta: + fmt.Printf("=> beta ") + pretty.PrettyPrint2Floats(v.a, v.b) + case Scalar: + fmt.Printf("=> scalar ") + w := float64(v) + pretty.PrettyPrintFloat(w) + fmt.Println() + default: + fmt.Printf("%v", v) } - return xs -} - -func (ln Lognormal) Samples() []float64 { - sampler := func(r sample.Src) float64 { return sample.Sample_to(ln.low, ln.high, r) } - // return sample.Sample_parallel(sampler, N_SAMPLES) - // Can't do parallel because then I'd have to await throughout the code - return sample.Sample_serially(sampler, N_SAMPLES) -} - -func (beta Beta) Samples() []float64 { - sampler := func(r sample.Src) float64 { return sample.Sample_beta(beta.a, beta.b, r) } - // return sample.Sample_parallel(sampler, N_SAMPLES) - return sample.Sample_serially(sampler, N_SAMPLES) -} - -func (fs FilledSamples) Samples() []float64 { - return fs.xs } // Parse line into Distribution - func parseLineErr(err_msg string) (string, Dist, error) { fmt.Println(GENERAL_ERR_MSG) fmt.Println(err_msg) @@ -253,8 +290,10 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { { switch n := new_dist.(type) { case Lognormal: + // to do: check division by zero return multiplyLogDists(o, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil case Scalar: + // to do: check division by zero return multiplyLogDists(o, Lognormal{low: 1.0 / float64(n), high: 1.0 / float64(n)}), nil default: return operateDistsAsSamples(old_dist, new_dist, "/") @@ -266,6 +305,7 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { case Lognormal: return multiplyLogDists(Lognormal{low: float64(o), high: float64(o)}, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil case Scalar: + // to do: check division by zero return Scalar(float64(o) / float64(n)), nil default: return operateDistsAsSamples(old_dist, new_dist, "/") @@ -276,92 +316,9 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) { } } -/* Pretty print distributions - */ -func prettyPrintInt(n int) { - switch { - case math.Abs(float64(n)) >= 1_000_000_000_000: - fmt.Printf("%dT", n/1_000_000_000_000) - case math.Abs(float64(n)) >= 1_000_000_000: - fmt.Printf("%dB", n/1_000_000_000) - case math.Abs(float64(n)) >= 1_000_000: - fmt.Printf("%dM", n/1_000_000) - case math.Abs(float64(n)) >= 1_000: - fmt.Printf("%dK", n/1_000) - default: - fmt.Printf("%df", n) - } -} - -func prettyPrintFloat(f float64) { - switch { - case math.Abs(f) >= 1_000_000_000_000: - fmt.Printf("%.1fT", f/1_000_000_000_000) - case math.Abs(f) >= 1_000_000_000: - fmt.Printf("%.1fB", f/1_000_000_000) - case math.Abs(f) >= 1_000_000: - fmt.Printf("%.1fM", f/1_000_000) - case math.Abs(f) >= 1_000: - fmt.Printf("%.1fK", f/1_000) - - case math.Abs(f) <= 0.0001: - fmt.Printf("%.5f", f) - case math.Abs(f) <= 0.001: - fmt.Printf("%.4f", f) - case math.Abs(f) <= 0.01: - fmt.Printf("%.3f", f) - case math.Abs(f) <= 0.1: - fmt.Printf("%.2f", f) - default: - fmt.Printf("%.1f", f) - } - -} -func prettyPrint2Floats(low float64, high float64) { - prettyPrintFloat(low) - fmt.Printf(" ") - prettyPrintFloat(high) - fmt.Printf("\n") -} - -func prettyPrintDist(dist Dist) { - switch v := dist.(type) { - case Lognormal: - fmt.Printf("=> ") - prettyPrint2Floats(v.low, v.high) - case FilledSamples: - tmp_xs := make([]float64, N_SAMPLES) - copy(tmp_xs, v.xs) - sort.Slice(tmp_xs, func(i, j int) bool { - return tmp_xs[i] < tmp_xs[j] - }) - low_int := N_SAMPLES / 20 - low := tmp_xs[low_int] - high_int := N_SAMPLES * 19 / 20 - high := tmp_xs[high_int] - fmt.Printf("=> ") - prettyPrintFloat(low) - fmt.Printf(" ") - prettyPrintFloat(high) - fmt.Printf(" (") - prettyPrintInt(N_SAMPLES) - fmt.Printf(" samples)\n") - case Beta: - fmt.Printf("=> beta ") - prettyPrint2Floats(v.a, v.b) - case Scalar: - fmt.Printf("=> scalar ") - w := float64(v) - prettyPrintFloat(w) - fmt.Println() - default: - fmt.Printf("%v", v) - } -} - /* Combine old dist and new line */ // We want this as a function to be able to have parenthesis/recusion, possibly functions -func combineStackAndDist(stack Stack, new_dist Dist, op string) Stack { +func operateStackWithDist(stack Stack, new_dist Dist, op string) Stack { var combined_dist Dist var err error @@ -403,7 +360,7 @@ func runRepl(stack Stack, reader *bufio.Reader) Stack { /* Parenthesis */ case len(words) == 2 && (words[0] == "*" || words[0] == "+" || words[0] == "-" || words[0] == "/") && words[1] == "(": new_stack := runRepl(Stack{old_dist: INIT_DIST, vars: stack.vars}, reader) - stack = combineStackAndDist(stack, new_stack.old_dist, words[0]) + stack = operateStackWithDist(stack, new_stack.old_dist, words[0]) prettyPrintDist(stack.old_dist) case len(words) == 1 && words[0] == ")": return stack @@ -439,7 +396,7 @@ func runRepl(stack Stack, reader *bufio.Reader) Stack { if err != nil { continue replForLoop } - stack = combineStackAndDist(stack, new_dist, op) + stack = operateStackWithDist(stack, new_dist, op) prettyPrintDist(stack.old_dist) } } diff --git a/pretty/pretty.go b/pretty/pretty.go @@ -0,0 +1,52 @@ +package pretty + +import ( + "fmt" + "math" +) + +func PrettyPrintInt(n int) { + switch { + case math.Abs(float64(n)) >= 1_000_000_000_000: + fmt.Printf("%dT", n/1_000_000_000_000) + case math.Abs(float64(n)) >= 1_000_000_000: + fmt.Printf("%dB", n/1_000_000_000) + case math.Abs(float64(n)) >= 1_000_000: + fmt.Printf("%dM", n/1_000_000) + case math.Abs(float64(n)) >= 1_000: + fmt.Printf("%dK", n/1_000) + default: + fmt.Printf("%df", n) + } +} + +func PrettyPrintFloat(f float64) { + switch { + case math.Abs(f) >= 1_000_000_000_000: + fmt.Printf("%.1fT", f/1_000_000_000_000) + case math.Abs(f) >= 1_000_000_000: + fmt.Printf("%.1fB", f/1_000_000_000) + case math.Abs(f) >= 1_000_000: + fmt.Printf("%.1fM", f/1_000_000) + case math.Abs(f) >= 1_000: + fmt.Printf("%.1fK", f/1_000) + + case math.Abs(f) <= 0.0001: + fmt.Printf("%.5f", f) + case math.Abs(f) <= 0.001: + fmt.Printf("%.4f", f) + case math.Abs(f) <= 0.01: + fmt.Printf("%.3f", f) + case math.Abs(f) <= 0.1: + fmt.Printf("%.2f", f) + default: + fmt.Printf("%.1f", f) + } + +} +func PrettyPrint2Floats(low float64, high float64) { + PrettyPrintFloat(low) + fmt.Printf(" ") + PrettyPrintFloat(high) + fmt.Printf("\n") +}