Skip to content
Snippets Groups Projects
Commit bd7dd92b authored by Andrew E. Torda's avatar Andrew E. Torda
Browse files

Moved plotting into a separate file.

Removed buffering of stdout. This is for students to read, rather than speed.
parent 4d3e2e81
Branches
No related tags found
No related merge requests found
// 29 april 2021
// If I get time, it would be fun to do the second uebung in go.
// An example for the second Uebung in Struktur und Simulation.
// For the sake of speed, we can do all the numbers at once, put
// them in arrays, rather than doing i/o on every step.
// them in arrays, rather than doing i/o on every step. This is actually
// necessary if we want to make plots. We have to store all the points.
// The plotting library I used does not draw to the screen. Bummer. One could
// use the canvas in vggio, although that pulls in all of fyne.
// use the canvas in vggio, although that pulls in all of the fyne library.
// There is an egregious bug in the clipping of lines in plotter, so do
// not use it. Use a scatter plot.
package main
import (
"bufio"
"errors"
"flag"
"fmt"
......@@ -16,13 +18,6 @@ import (
"math/rand"
"os"
"strconv"
"gonum.org/v1/plot"
"gonum.org/v1/plot/font"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
)
const (
......@@ -30,18 +25,12 @@ const (
exitFailure
)
const (
sizeHrznt font.Length = 15 * vg.Centimeter
sizeVert font.Length = 10 * vg.Centimeter
nPointPlot = 1000
)
func breaker(x interface{}) {}
// usage prints out anything the caller gives us then bails.
func usage(e string) {
u := "usage: pi seed nsteps nstep_nprint"
u := "usage: pi [flags] seed nsteps nstep_nprint"
if e != ""{
fmt.Fprintln(os.Stderr, e+"\n"+u)
}
flag.Usage()
os.Exit(exitFailure)
}
......@@ -60,7 +49,7 @@ func getnums(cmdArgs cmdArgs) ([]float32, []float32) {
}
}
if inPoint[0] {
inCnt[0] = 1
inCnt[0] = 1 // avoid divide by zero errors in loop
}
for i := 1; i < cmdArgs.nstep; i++ {
inCnt[i] = inCnt[i-1]
......@@ -73,98 +62,20 @@ func getnums(cmdArgs cmdArgs) ([]float32, []float32) {
stdErr := make([]float32, cmdArgs.nstep) // standard error
for i := range inCnt {
ptmp := float64(inCnt[i]) / float64(i+1) // do we need more precision ?
qtmp := 1 - ptmp
ptmp := float64(inCnt[i]) / float64(i+1)
pi[i] = float32(4 * ptmp)
stdErr[i] = float32(4 * math.Sqrt((ptmp*qtmp)/(float64(i+1))))
stdErr[i] = float32(4 * math.Sqrt((ptmp*(1-ptmp))/(float64(i+1))))
}
return pi, stdErr
}
// wrtTable prints out the table, as per the Uebung instructions
func wrtTable(cmdArgs cmdArgs, pi, stdErr []float32) {
b := bufio.NewWriter(os.Stdout)
b.WriteString(fmt.Sprintf("%8s %7s %7s %7s\n", "step", "pi", "err", "stderr"))
defer b.Flush()
fmt.Printf("%8s %7s %7s %7s\n", "step", "pi", "err", "stderr")
for i := 0; i < cmdArgs.nstep; i = i + cmdArgs.nstepPrint {
e := math.Pi - pi[i]
b.WriteString(fmt.Sprintf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i]))
}
}
// addline saves us doing the same point copying, adding three times.
func oneplot(y []float32) (*plot.Plot, error) {
p := plot.New()
points := make(plotter.XYs, len(y))
for i, piV := range y {
points[i] = plotter.XY{X: float64(i + 1), Y: float64(piV)}
}
if line, err := plotter.NewLine(points); err != nil {
return nil, err
} else {
line.LineStyle.Width = 4
p.Add(line)
fmt.Printf("%8d %.5f %.5f %.5f\n", i+1, pi[i], e, stdErr[i])
}
return p, nil
}
// doplot makes a primitive plot without any interesting options
// If no plotname is given, it means no plot is wanted. We have to rewrite the data
// to fit to the plot function's expectations (x/y pairs).
func doplot(pi, stdErr []float32, plotName string) error {
if plotName == "" {
return nil
}
const step = "step"
pPi, err := oneplot(pi) // pi estimate
if err != nil {
return err
}
pPi.Title.Text = ""
pPi.X.Label.Text = step
pPi.Y.Label.Text = "π estimate"
pStd, err := oneplot(stdErr) // std error
if err != nil {
return err
}
pStd.Title.Text = ""
pStd.X.Label.Text = step
pStd.Y.Label.Text = "std error"
pAbs := plot.New()
{
tmperr := make([]float32, len(pi)) // the extra bracing makes
for i := range tmperr { // the lifetime of tmperr clear...
tmperr[i] = pi[i] - math.Pi // for garbage collector
}
pAbs, err = oneplot(tmperr)
if err != nil {
return err
}
}
pAbs.X.Label.Text = step
pAbs.Y.Label.Text = "error"
dt := draw.Tiles{Rows: 3, Cols: 1, PadTop: 2}
img := vgimg.New(vg.Points(600), vg.Points(500))
dc := draw.New(img)
dCnvs := plot.Align([][]*plot.Plot{{pPi}, {pStd}, {pAbs}}, dt, dc)
pPi.Draw(dCnvs[0][0])
pStd.Draw(dCnvs[1][0])
pAbs.Draw(dCnvs[2][0])
w, err := os.Create(plotName)
if err != nil {
return fmt.Errorf("Opening plotfile for writing %w", err)
}
defer w.Close()
breaker("before vgimg")
jpg := vgimg.JpegCanvas{Canvas: img}
if _, err := jpg.WriteTo(w); err != nil {
return err
}
pStd.Save(vg.Points(150), vg.Points(250), "foo.png")
return nil
}
type cmdArgs struct {
......@@ -178,8 +89,8 @@ type cmdArgs struct {
func cmdline(cmdArgs *cmdArgs) error {
var err error
var suppress bool
flag.StringVar(&cmdArgs.plotName, "p", "", "Plot filename")
flag.BoolVar(&suppress, "s", false, "Suppress stdout")
flag.StringVar(&cmdArgs.plotName, "p", "", "filename to Plot to. No name means no plofile")
flag.BoolVar(&suppress, "s", false, "Suppress stdout - the long table of numbers")
flag.Parse()
if flag.NArg() != 3 {
return errors.New("Wrong number of command line args")
......@@ -201,7 +112,6 @@ func cmdline(cmdArgs *cmdArgs) error {
return nil
}
// main. The rules say ./pi seed nsteps nstep_print
func main() {
var cmdArgs cmdArgs
if err := cmdline(&cmdArgs); err != nil {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment