Compare commits
3 Commits
3a734f180c
...
58f6e73d3f
Author | SHA1 | Date | |
---|---|---|---|
58f6e73d3f | |||
6c88fbf2ec | |||
8720530d4e |
46
README.md
46
README.md
|
@ -1,4 +1,4 @@
|
|||
# A calculator for distributions, for Fermi estimation
|
||||
# A terminal calculator for distributions, for Fermi estimation
|
||||
|
||||
This project is a minimalist, calculator-style DSL for fermi estimation. It can multiply, divide, add and subtract scalars, lognormals and beta distributions, and supports variables.
|
||||
|
||||
|
@ -6,36 +6,22 @@ This project is a minimalist, calculator-style DSL for fermi estimation. It can
|
|||
|
||||
Sometimes, [Squiggle](https://github.com/quantified-uncertainty/squiggle), [simple squiggle](https://git.nunosempere.com/quantified.uncertainty/simple-squiggle) or [squiggle.c](https://git.nunosempere.com/personal/squiggle.c) are still too complicated and un-unix-like. In particular, their startup cost is not instant.
|
||||
|
||||
## Installation
|
||||
## Build instructions
|
||||
|
||||
Install the [go toolchain](https://go.dev/dl/), then:
|
||||
|
||||
|
||||
```
|
||||
git clone https://git.nunosempere.com/NunoSempere/fermi
|
||||
cd fermi
|
||||
make build
|
||||
sudo make install
|
||||
fermi
|
||||
./fermi
|
||||
# sudo make install
|
||||
# fermi
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
$ fermi
|
||||
5000000 12000000
|
||||
=> 5.0M 12.0M
|
||||
* beta 1 200
|
||||
=> 1.9K 123.1K
|
||||
* 30 180
|
||||
=> 122.9K 11.7M
|
||||
/ 48 52
|
||||
=> 2.5K 234.6K
|
||||
/ 5 6
|
||||
=> 448.8 43.0K
|
||||
/ 6 8
|
||||
=> 64.5 6.2K
|
||||
/ 60
|
||||
=> 1.1 103.7
|
||||
```
|
||||
|
||||
Perhaps this example is more understandable with comments and better units:
|
||||
|
||||
```
|
||||
$ fermi
|
||||
5M 12M # number of people living in Chicago
|
||||
|
@ -48,10 +34,12 @@ beta 1 200 # fraction of people that have a piano
|
|||
=: piano_tuners
|
||||
```
|
||||
|
||||
Here are some real-life examples: [Chance for a Russian male of fighting age of being drafted](https://x.com/NunoSempere/status/1829525844169248912), [did the startup Friend burn too much cash](https://x.com/NunoSempere/status/1818810770932568308), [how much did Nikita Bier make mentoring?](https://x.com/NunoSempere/status/1815169781907042504), [what fraction of North Korea's caloric intake is Russia supporting?](https://x.com/NunoSempere/status/1855666428835140078). In general, as a terminal guy, I've found that having zero startup cost makes creating small fermi models much cheaper, and thus happen more often.
|
||||
|
||||
If you type "help" (or run fermi -h), you can see a small grammar and some optional command flags:
|
||||
|
||||
```
|
||||
$ fermi
|
||||
$ fermi -h
|
||||
|
||||
1. Grammar:
|
||||
Operation | Variable assignment | Special
|
||||
|
@ -88,6 +76,7 @@ $ fermi
|
|||
1 10
|
||||
+ beta 1 100
|
||||
)
|
||||
/ 1%
|
||||
=. y
|
||||
mx x 1 y 2.33
|
||||
+ mx x 30% y 70%
|
||||
|
@ -104,12 +93,11 @@ $ fermi
|
|||
|
||||
```
|
||||
|
||||
You can see real life examples [here](https://x.com/NunoSempere/status/1831106442721452312), [here](https://x.com/NunoSempere/status/1829525844169248912), [here](https://x.com/NunoSempere/status/1818810770932568308), [here](https://x.com/NunoSempere/status/1816605190415401100), [here](https://x.com/NunoSempere/status/1816604386703081894), [here](https://x.com/NunoSempere/status/1815169781907042504)
|
||||
|
||||
## Tips & tricks
|
||||
|
||||
- It's conceptually clearer to have all the multiplications first and then all the divisions
|
||||
- For distributions between 0 and 1, consider using a beta distribution
|
||||
- The default operation is multiplication
|
||||
|
||||
### Command line options
|
||||
|
||||
|
@ -197,11 +185,11 @@ Done:
|
|||
- [x] Add percentages
|
||||
- [x] Consider adding an understanding of percentages
|
||||
- [x] Improve and rationalize error messages a bit
|
||||
- [x] Add, then document mixture distributions
|
||||
|
||||
To (possibly) do:
|
||||
|
||||
- [ ] Consider implications of sampling strategy for operating variables in this case.
|
||||
- [ ] Document mixture distributions
|
||||
- [ ] Consider implications of sampling strategy for operating variables.
|
||||
- [ ] Fix lognormal multiplication and division by 0 or < 0
|
||||
- [ ] With the -f command line option, the program doesn't read from stdin after finishing reading the file
|
||||
- [ ] Add functions. Now easier to do with an explicit representation of the stack
|
||||
|
|
2
makefile
2
makefile
|
@ -2,7 +2,7 @@ build:
|
|||
go build main/fermi.go main/pretty.go main/error.go
|
||||
|
||||
run:
|
||||
go run fermi.go
|
||||
go run main/fermi.go main/error.go main/pretty.go
|
||||
|
||||
install: fermi
|
||||
rm /usr/bin/fermi
|
||||
|
|
135
simple/simple_pretty.go
Normal file
135
simple/simple_pretty.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
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 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user