Compare commits
7 Commits
a99934387c
...
84bdfa004f
Author | SHA1 | Date | |
---|---|---|---|
84bdfa004f | |||
ae1e1bbe97 | |||
6c98249e57 | |||
4b21612d55 | |||
c3f03d97b0 | |||
05cbc2029d | |||
27f9932db8 |
10
README.md
10
README.md
|
@ -4,7 +4,7 @@ This project is a minimalist, calculator-style DSL for fermi estimation. It can
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
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.
|
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
|
## Installation
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ $ fermi
|
||||||
beta: beta alpha beta
|
beta: beta alpha beta
|
||||||
Variable assignment: =: variable_name
|
Variable assignment: =: variable_name
|
||||||
Variable assignment and clear stack: =. variable_name
|
Variable assignment and clear stack: =. variable_name
|
||||||
|
Suffixes: %, K, M, B, T
|
||||||
Special commands:
|
Special commands:
|
||||||
Comment: # this is a comment
|
Comment: # this is a comment
|
||||||
Summary stats: stats
|
Summary stats: stats
|
||||||
|
@ -94,7 +95,7 @@ Command flags:
|
||||||
-echo
|
-echo
|
||||||
Specifies whether inputs should be echoed back. Useful if reading from a file
|
Specifies whether inputs should be echoed back. Useful if reading from a file
|
||||||
. -f string
|
. -f string
|
||||||
Specifies a file with a model to run
|
Specifies a file with a model to run. Sets the echo command to true by default.
|
||||||
-n int
|
-n int
|
||||||
Specifies the number of samples to draw when using samples (default 100000)
|
Specifies the number of samples to draw when using samples (default 100000)
|
||||||
-h Shows help message
|
-h Shows help message
|
||||||
|
@ -191,15 +192,20 @@ Done:
|
||||||
- [x] Figure out how to make models executable, by adding a #!/bin/bash-style command at the top?
|
- [x] Figure out how to make models executable, by adding a #!/bin/bash-style command at the top?
|
||||||
- [x] Make -n flag work
|
- [x] Make -n flag work
|
||||||
- [x] Add flag to repeat input lines (useful when reading from files)
|
- [x] Add flag to repeat input lines (useful when reading from files)
|
||||||
|
- [x] Add percentages
|
||||||
|
|
||||||
To (possibly) do:
|
To (possibly) do:
|
||||||
|
|
||||||
|
- [ ] Fix lognormal multiplication and division by 0 or < 0
|
||||||
|
- [ ] Consider adding an understanding of percentages
|
||||||
|
- [ ] 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 stakc
|
- [ ] Add functions. Now easier to do with an explicit representation of the stakc
|
||||||
- [ ] Think about how to draw a histogram from samples
|
- [ ] Think about how to draw a histogram from samples
|
||||||
- [ ] Dump samples to file
|
- [ ] Dump samples to file
|
||||||
- [ ] Represent samples/statistics in some other way
|
- [ ] Represent samples/statistics in some other way
|
||||||
- [ ] Perhaps use qsort rather than full sorting
|
- [ ] Perhaps use qsort rather than full sorting
|
||||||
- [ ] Program into a small device, like a calculator?
|
- [ ] Program into a small device, like a calculator?
|
||||||
|
- [ ] Units?
|
||||||
|
|
||||||
Discarded:
|
Discarded:
|
||||||
|
|
||||||
|
|
37
fermi.go
37
fermi.go
|
@ -254,6 +254,16 @@ func multiplyBetaDists(beta1 Beta, beta2 Beta) Beta {
|
||||||
return Beta{a: beta1.a + beta2.a, b: beta1.b + beta2.b}
|
return Beta{a: beta1.a + beta2.a, b: beta1.b + beta2.b}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func multiplyLogDistAndScalar(l Lognormal, s Scalar) (Dist, error) {
|
||||||
|
if s == 0.0 {
|
||||||
|
return Scalar(0.0), nil
|
||||||
|
} else if s < 0.0 {
|
||||||
|
return operateDistsAsSamples(s, l, "+")
|
||||||
|
} else {
|
||||||
|
return multiplyLogDists(l, Lognormal{low: float64(s), high: float64(s)}), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func multiplyDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
func multiplyDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
||||||
|
|
||||||
switch o := old_dist.(type) {
|
switch o := old_dist.(type) {
|
||||||
|
@ -263,17 +273,20 @@ func multiplyDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
||||||
case Lognormal:
|
case Lognormal:
|
||||||
return multiplyLogDists(o, n), nil
|
return multiplyLogDists(o, n), nil
|
||||||
case Scalar:
|
case Scalar:
|
||||||
return multiplyLogDists(o, Lognormal{low: float64(n), high: float64(n)}), nil
|
return multiplyLogDistAndScalar(o, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Scalar:
|
case Scalar:
|
||||||
{
|
{
|
||||||
if o == 1 {
|
switch o {
|
||||||
|
case 1.0:
|
||||||
return new_dist, nil
|
return new_dist, nil
|
||||||
|
case 0.0:
|
||||||
|
return Scalar(0.0), nil
|
||||||
}
|
}
|
||||||
switch n := new_dist.(type) {
|
switch n := new_dist.(type) {
|
||||||
case Lognormal:
|
case Lognormal:
|
||||||
return multiplyLogDists(Lognormal{low: float64(o), high: float64(o)}, n), nil
|
return multiplyLogDistAndScalar(n, o)
|
||||||
case Scalar:
|
case Scalar:
|
||||||
return Scalar(float64(o) * float64(n)), nil
|
return Scalar(float64(o) * float64(n)), nil
|
||||||
}
|
}
|
||||||
|
@ -304,14 +317,14 @@ func divideDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
||||||
fmt.Println("Error: Can't divide by 0.0")
|
fmt.Println("Error: Can't divide by 0.0")
|
||||||
return nil, errors.New("Error: division by zero scalar")
|
return nil, errors.New("Error: division by zero scalar")
|
||||||
}
|
}
|
||||||
return multiplyLogDists(o, Lognormal{low: 1.0 / float64(n), high: 1.0 / float64(n)}), nil
|
return multiplyLogDistAndScalar(o, Scalar(1.0/n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Scalar:
|
case Scalar:
|
||||||
{
|
{
|
||||||
switch n := new_dist.(type) {
|
switch n := new_dist.(type) {
|
||||||
case Lognormal:
|
case Lognormal:
|
||||||
return multiplyLogDists(Lognormal{low: float64(o), high: float64(o)}, Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}), nil
|
return multiplyLogDistAndScalar(Lognormal{low: 1.0 / n.high, high: 1.0 / n.low}, o)
|
||||||
case Scalar:
|
case Scalar:
|
||||||
if n == 0.0 {
|
if n == 0.0 {
|
||||||
fmt.Println("Error: Can't divide by 0.0")
|
fmt.Println("Error: Can't divide by 0.0")
|
||||||
|
@ -374,8 +387,15 @@ func parseWordsIntoOpAndDist(words []string, vars map[string]Dist) (string, Dist
|
||||||
case 2:
|
case 2:
|
||||||
new_low, err1 := pretty.ParseFloat(words[0])
|
new_low, err1 := pretty.ParseFloat(words[0])
|
||||||
new_high, err2 := pretty.ParseFloat(words[1])
|
new_high, err2 := pretty.ParseFloat(words[1])
|
||||||
if err1 != nil || err2 != nil {
|
switch {
|
||||||
|
case err1 != nil || err2 != nil:
|
||||||
return parseWordsErr("Trying to operate by a distribution, but distribution is not specified as two floats")
|
return parseWordsErr("Trying to operate by a distribution, but distribution is not specified as two floats")
|
||||||
|
case new_low <= 0.0 || new_high <= 0.0:
|
||||||
|
return parseWordsErr("Trying to parse two floats as a lognormal, but the two floats must be greater than 0")
|
||||||
|
case new_low == new_high:
|
||||||
|
return parseWordsErr("Trying to parse two floats as a lognormal, but the two floats must be different. Try a single scalar instead?")
|
||||||
|
case new_low > new_high:
|
||||||
|
return parseWordsErr("Trying to parse two floats as a lognormal, but the first number is larger than the second number")
|
||||||
}
|
}
|
||||||
dist = Lognormal{low: new_low, high: new_high}
|
dist = Lognormal{low: new_low, high: new_high}
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -404,7 +424,7 @@ replForLoop:
|
||||||
for {
|
for {
|
||||||
new_line, _ := reader.ReadString('\n')
|
new_line, _ := reader.ReadString('\n')
|
||||||
if *echo_flag {
|
if *echo_flag {
|
||||||
fmt.Printf(new_line)
|
fmt.Print(new_line)
|
||||||
}
|
}
|
||||||
new_line_before_comments, _, _ := strings.Cut(new_line, "#")
|
new_line_before_comments, _, _ := strings.Cut(new_line, "#")
|
||||||
new_line_trimmed := strings.TrimSpace(new_line_before_comments)
|
new_line_trimmed := strings.TrimSpace(new_line_before_comments)
|
||||||
|
@ -459,7 +479,7 @@ replForLoop:
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
num_samples_flag := flag.Int("n", N_SAMPLES, "Specifies the number of samples to draw when using samples")
|
num_samples_flag := flag.Int("n", N_SAMPLES, "Specifies the number of samples to draw when using samples")
|
||||||
filename := flag.String("f", "", "Specifies a file with a model to run")
|
filename := flag.String("f", "", "Specifies a file with a model to run. Sets the echo flag to true")
|
||||||
echo_flag := flag.Bool("echo", false, "Specifies whether inputs should be echoed back. Useful if reading from a file.")
|
echo_flag := flag.Bool("echo", false, "Specifies whether inputs should be echoed back. Useful if reading from a file.")
|
||||||
help_flag := flag.Bool("h", false, "Shows help message")
|
help_flag := flag.Bool("h", false, "Shows help message")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -472,6 +492,7 @@ func main() {
|
||||||
if *filename != "" {
|
if *filename != "" {
|
||||||
file, err := os.Open(*filename)
|
file, err := os.Open(*filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
*echo_flag = true
|
||||||
reader = bufio.NewReader(file)
|
reader = bufio.NewReader(file)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Error opening filename; reading from stdin instead\n")
|
fmt.Printf("Error opening filename; reading from stdin instead\n")
|
||||||
|
|
|
@ -72,6 +72,8 @@ func ParseFloat(word string) (float64, error) {
|
||||||
n := len(word) - 1
|
n := len(word) - 1
|
||||||
f, err := strconv.ParseFloat(word[:n], 64)
|
f, err := strconv.ParseFloat(word[:n], 64)
|
||||||
switch word[n] {
|
switch word[n] {
|
||||||
|
case '%':
|
||||||
|
return multiplyOrPassThroughError(0.01, f, err)
|
||||||
case 'K':
|
case 'K':
|
||||||
return multiplyOrPassThroughError(1_000, f, err)
|
return multiplyOrPassThroughError(1_000, f, err)
|
||||||
case 'M':
|
case 'M':
|
||||||
|
|
Loading…
Reference in New Issue
Block a user