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
|
||||
|
||||
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
|
||||
|
||||
|
@ -62,6 +62,7 @@ $ fermi
|
|||
beta: beta alpha beta
|
||||
Variable assignment: =: variable_name
|
||||
Variable assignment and clear stack: =. variable_name
|
||||
Suffixes: %, K, M, B, T
|
||||
Special commands:
|
||||
Comment: # this is a comment
|
||||
Summary stats: stats
|
||||
|
@ -94,7 +95,7 @@ Command flags:
|
|||
-echo
|
||||
Specifies whether inputs should be echoed back. Useful if reading from a file
|
||||
. -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
|
||||
Specifies the number of samples to draw when using samples (default 100000)
|
||||
-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] Make -n flag work
|
||||
- [x] Add flag to repeat input lines (useful when reading from files)
|
||||
- [x] Add percentages
|
||||
|
||||
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
|
||||
- [ ] Think about how to draw a histogram from samples
|
||||
- [ ] Dump samples to file
|
||||
- [ ] Represent samples/statistics in some other way
|
||||
- [ ] Perhaps use qsort rather than full sorting
|
||||
- [ ] Program into a small device, like a calculator?
|
||||
- [ ] Units?
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
switch o := old_dist.(type) {
|
||||
|
@ -263,17 +273,20 @@ func multiplyDists(old_dist Dist, new_dist Dist) (Dist, error) {
|
|||
case Lognormal:
|
||||
return multiplyLogDists(o, n), nil
|
||||
case Scalar:
|
||||
return multiplyLogDists(o, Lognormal{low: float64(n), high: float64(n)}), nil
|
||||
return multiplyLogDistAndScalar(o, n)
|
||||
}
|
||||
}
|
||||
case Scalar:
|
||||
{
|
||||
if o == 1 {
|
||||
switch o {
|
||||
case 1.0:
|
||||
return new_dist, nil
|
||||
case 0.0:
|
||||
return Scalar(0.0), nil
|
||||
}
|
||||
switch n := new_dist.(type) {
|
||||
case Lognormal:
|
||||
return multiplyLogDists(Lognormal{low: float64(o), high: float64(o)}, n), nil
|
||||
return multiplyLogDistAndScalar(n, o)
|
||||
case Scalar:
|
||||
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")
|
||||
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:
|
||||
{
|
||||
switch n := new_dist.(type) {
|
||||
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:
|
||||
if n == 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:
|
||||
new_low, err1 := pretty.ParseFloat(words[0])
|
||||
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")
|
||||
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}
|
||||
case 3:
|
||||
|
@ -404,7 +424,7 @@ replForLoop:
|
|||
for {
|
||||
new_line, _ := reader.ReadString('\n')
|
||||
if *echo_flag {
|
||||
fmt.Printf(new_line)
|
||||
fmt.Print(new_line)
|
||||
}
|
||||
new_line_before_comments, _, _ := strings.Cut(new_line, "#")
|
||||
new_line_trimmed := strings.TrimSpace(new_line_before_comments)
|
||||
|
@ -459,7 +479,7 @@ replForLoop:
|
|||
|
||||
func main() {
|
||||
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.")
|
||||
help_flag := flag.Bool("h", false, "Shows help message")
|
||||
flag.Parse()
|
||||
|
@ -472,6 +492,7 @@ func main() {
|
|||
if *filename != "" {
|
||||
file, err := os.Open(*filename)
|
||||
if err == nil {
|
||||
*echo_flag = true
|
||||
reader = bufio.NewReader(file)
|
||||
} else {
|
||||
fmt.Printf("Error opening filename; reading from stdin instead\n")
|
||||
|
|
|
@ -72,6 +72,8 @@ func ParseFloat(word string) (float64, error) {
|
|||
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':
|
||||
|
|
Loading…
Reference in New Issue
Block a user