From 1b2c6cb9920b527650f7addc0b9a2ffc1000ac8f Mon Sep 17 00:00:00 2001
From: "Andrew E. Torda" <torda@zbh.uni-hamburg.de>
Date: Tue, 22 Feb 2022 17:45:50 +0100
Subject: [PATCH] Results from MC are now returned in a structure which
 includes the number of moves and acceptance.

---
 mc_work/ToDo              |  7 ------
 mc_work/dorun.go          | 24 ++++++------------
 mc_work/mc_work.go        | 24 +++++++++---------
 mc_work/plotres.r         |  2 +-
 mc_work/realmain_nogfx.go | 12 ++++++---
 mc_work/set_suffix.go     |  2 --
 ui/input_tab.go           | 25 +++++++++++--------
 ui/mymain.go              |  2 --
 ui/output_tab.go          | 52 ++++++++++++++++-----------------------
 ui/ui_run.go              | 16 ++++++++++++
 10 files changed, 80 insertions(+), 86 deletions(-)

diff --git a/mc_work/ToDo b/mc_work/ToDo
index bec88f2..9ab631a 100644
--- a/mc_work/ToDo
+++ b/mc_work/ToDo
@@ -3,11 +3,4 @@ dlv test --build-flags "-tags=no_gfx"
 
 In dorun.go
 
-* do we have the random number seed as a settable parameter ?
-
-* make the csv files optional
-
 Note.. Benchmarking suggests that most of the time is spent in Ackley function, and, to be exact, in the cosine and exp() function.
-
-In the plottin functions,
-Put a secondary axis on the X trajectory or check out multiplots so that the two plots line up with each other.
diff --git a/mc_work/dorun.go b/mc_work/dorun.go
index 0597b26..042d982 100644
--- a/mc_work/dorun.go
+++ b/mc_work/dorun.go
@@ -135,16 +135,6 @@ func setupRun(mcPrm *McPrm, cprm *cprm) error {
 	return nil
 }
 
-// altnewx is not used at the moment, but I will build a switch for it.
-// It is like newx, but moves all n dimensions a bit.
-func alt_newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float32) {
-	for i, x := range xold {
-		t := 2.*rand.Float32() - 1
-		t *= xDlta
-		xT[i] = x + t
-	}
-}
-
 // newx will move just one coordinate at a time
 func newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float64) {
 	var iDim int
@@ -203,10 +193,11 @@ func nRunAdj(mcPrm *McPrm, nstep int, runAcc float64, xDlta float64) float64 {
 // coordinates and function, we use double precision for the temperature.
 // Unfortunately, the plotting functions want double precision, so we store
 // an awful lot of stuff as float64.
-func DoRun(mcPrm *McPrm) ([]byte, []byte, error) {
+func DoRun(mcPrm *McPrm) (MCresult, error) {
 	var cprm cprm
+	broken := MCresult{}
 	if err := setupRun(mcPrm, &cprm); err != nil {
-		return nil, nil, err
+		return broken, err
 	}
 	if cprm.doTxtFval { // The text file documenting the run
 		defer cprm.fOutRaw.Close()
@@ -271,13 +262,13 @@ func DoRun(mcPrm *McPrm) ([]byte, []byte, error) {
 	if fTrial != fOld { // last move was not accepted, so we have to add last step
 		saveStep(&cprm, mcPrm.NStep, tmprtr, x, fOld)
 	}
-	defer fmt.Println("n accepted:", nAcc, "of", mcPrm.NStep+1)
+	defer fmt.Println("n accepted:", nAcc, "of", mcPrm.NStep)
 
 	if err := plotfWrt(&cprm); err != nil {
-		return nil, nil, err
+		return broken, err
 	}
 	if err := plotxWrt(&cprm, len(mcPrm.XIni)); err != nil {
-		return nil, nil, err
+		return broken, err
 	}
 
 	var fdata, xdata []byte
@@ -287,7 +278,6 @@ func DoRun(mcPrm *McPrm) ([]byte, []byte, error) {
 	if xBuf, ok := cprm.xplotWrt.(withBytes); ok {
 		xdata = xBuf.Bytes()
 	}
-	nothing(fdata, xdata)
 
-	return fdata, xdata, nil
+	return MCresult{ Fdata: fdata, Xdata: xdata, NStep: mcPrm.NStep, NAcc: nAcc}, nil
 }
diff --git a/mc_work/mc_work.go b/mc_work/mc_work.go
index f1c6c43..c3dada8 100644
--- a/mc_work/mc_work.go
+++ b/mc_work/mc_work.go
@@ -2,14 +2,10 @@
 
 package mcwork
 
-import (
-	"flag"
-	"fmt"
-	"os"
-)
-
-// Have to export everything, including the seed,
-// so the graphical interface can get to them
+// This is the structure which holds everything we read in to control
+// a Monte Carlo run.
+// Most things have to be exported so the graphical interface can get
+// to them.
 type McPrm struct {
 	IniTmp, FnlTmp float64   // Initial and final temperatures
 	XIni           []float32 // initial x slice
@@ -23,10 +19,12 @@ type McPrm struct {
 	dummy          bool // for testing. If set, do not do a run
 }
 
-var Seed int // for random numbers, but each thread gets its own value
+// For random numbers. It is not in the main structure, since, if one ever writes
+// a threaded version, each thread can have its own value.
+var Seed int
 
-// usage
-func usage() {
-	u := `[options] [input_parameter_file]`
-	fmt.Fprintf(flag.CommandLine.Output(), "usage of %s: %s\n", os.Args[0], u)
+// For returning results
+type MCresult struct {
+	Fdata, Xdata []byte // png images of function and X data
+	NStep, NAcc int     // number of steps and accepted steps
 }
diff --git a/mc_work/plotres.r b/mc_work/plotres.r
index bada2a0..412c230 100644
--- a/mc_work/plotres.r
+++ b/mc_work/plotres.r
@@ -1,4 +1,4 @@
-# Obviously this only makes sense for local testing of some of the results
+# This only makes sense for local testing of some of the results
 # We can plot out the column f_val as a function of temperature, x-coordinates, ...
 
 names <- c("step", "temperature", "f_val", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10")
diff --git a/mc_work/realmain_nogfx.go b/mc_work/realmain_nogfx.go
index 81e7f10..351d63a 100644
--- a/mc_work/realmain_nogfx.go
+++ b/mc_work/realmain_nogfx.go
@@ -7,26 +7,32 @@
 package mcwork
 
 import (
+	"flag"
 	"fmt"
 	"io"
 	"os"
 )
 
 // realmain is the real main function. The program wants to read from a file,
-// but we can have it read from an io.Reader and then use the test functions
-// for playing with it.
+// but we can have it read from an io.Reader. This makes testing practical.
 func realmain(fp io.Reader) error {
 	var mcPrm McPrm
 	if err := RdPrm(fp, &mcPrm); err != nil {
 		return err
 	}
-	if _, _, err := DoRun(&mcPrm); err != nil {
+	if _, err := DoRun(&mcPrm); err != nil {
 		return err
 	}
 
 	return nil
 }
 
+// usage
+func usage() {
+	u := `[input_parameter_file]`
+	fmt.Fprintf(flag.CommandLine.Output(), "usage of %s: %s\n", os.Args[0], u)
+}
+
 // MyMain is called from the real main. It opens the input file, but passes a
 // reader to the function that does the work. This lets us call it from a test
 // function.
diff --git a/mc_work/set_suffix.go b/mc_work/set_suffix.go
index 154dd25..17f95ad 100644
--- a/mc_work/set_suffix.go
+++ b/mc_work/set_suffix.go
@@ -48,5 +48,3 @@ func removeQuotes(s string) string {
 	}
 	return s
 }
-
-func nothing(...interface{}) {}
diff --git a/ui/input_tab.go b/ui/input_tab.go
index fda90ff..bd934ce 100644
--- a/ui/input_tab.go
+++ b/ui/input_tab.go
@@ -69,31 +69,34 @@ func validateXini(s string) error {
 // This gets put on the list of things to do from the reload function.
 func floatItem(xAddr *float64, label string) (*widget.FormItem, func()) {
 	bind := binding.BindFloat(xAddr)
-	entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%.2f"))
+	entry := widget.NewEntryWithData(binding.FloatToStringWithFormat(bind, "%.3f"))
 	fitem := widget.NewFormItem(label, entry)
 	reloadfunc := func() { _ = bind.Reload() }
 	return fitem, reloadfunc
 }
 
 // intItem does the same wrapping as floatItem, but for ints
-func intItem(xAddr *int, label string) (*widget.FormItem, func()) {
+func intItem(xAddr *int, label string, htext string) (*widget.FormItem, func()) {
 	bind := binding.BindInt(xAddr)
 	entry := widget.NewEntryWithData(binding.IntToString(bind))
 	iItem := widget.NewFormItem(label, entry)
+	iItem.HintText = htext
 	reloadfunc := func() { _ = bind.Reload() }
 	return iItem, reloadfunc
 }
 
+var xCoordHint = "The starting point. A comma-separated list of floats. It is used to set the dimensionality"
 // paramBox sets up a screen full of parameters we can adjust.
 // It returns the screen and a function to be called to refresh it.
 // This is necessary, since there is a button to read parameters from
 // a file, which means all the values should be updated.
-func paramBox(genParams genParams) *widget.Form {
+func paramForm(genParams genParams) *widget.Form {
 	mcPrm := genParams.mcPrm
+
 	iniTmpItem, iniTmpReload := floatItem(&mcPrm.IniTmp, "initial temperature")
 	fnlTmpItem, fnlTmpReload := floatItem(&mcPrm.FnlTmp, "final temperature")
 	xDltaItem, xDltaReload := floatItem(&mcPrm.XDlta, "X delta")
-
+	xDltaItem.HintText = "Maximum step size in one dimension"
 	xIniStr := binding.NewString()
 	_ = xIniStr.Set(fslicestrng(mcPrm.XIni))
 	xIniEntry := widget.NewEntryWithData(xIniStr)
@@ -107,10 +110,11 @@ func paramBox(genParams genParams) *widget.Form {
 	}
 	xIniEntry.Validator = validateXini
 	xIniItem := widget.NewFormItem("initial X", xIniEntry)
+	xIniItem.HintText = xCoordHint
 
-	nStepItem, nStepReload := intItem(&mcPrm.NStep, "N steps")
-	seedItem, seedReload := intItem(&mcwork.Seed, "seed random numbers")
-
+	nStepItem, nStepReload := intItem(&mcPrm.NStep, "N steps", "number of steps in calculation")
+	seedItem, seedReload := intItem(&mcwork.Seed, "seed random numbers", "seed for random number generator")
+	
 	reloadPTab := func() {
 		iniTmpReload()
 		fnlTmpReload()
@@ -129,6 +133,7 @@ func paramBox(genParams genParams) *widget.Form {
 		nStepItem, seedItem, rdFileItem)
 	r.SubmitText = "start calculation"
 	r.OnSubmit = func() { startrun(genParams, r, mcPrm) }
+	reloadPTab() // does this help ? sometimes the form pops up with entries not ready
 	return r
 }
 
@@ -177,15 +182,15 @@ func rdwork(genParams genParams, reload func()) {
 func mcWrap(genParams genParams) {
 	chn := genParams.chn
 	chn <- workstatus{status: calculating}
-	if fdata, xdata, err := mcwork.DoRun(genParams.mcPrm); err != nil {
+	if result, err := mcwork.DoRun(genParams.mcPrm); err != nil {
 		dialog.NewError(err, genParams.win).Show()
 		chn <- workstatus{status: errorCalc, err: err}
 	} else {
-		chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady}
+		chn <- workstatus{ MCresult: result, status: resultsReady}
 	}
 }
 
 // inputTab sets up the input page.
 func inputTab(genParams genParams) *widget.Form {
-	return paramBox(genParams)
+	return paramForm(genParams)
 }
diff --git a/ui/mymain.go b/ui/mymain.go
index e17ff98..54493ea 100644
--- a/ui/mymain.go
+++ b/ui/mymain.go
@@ -6,7 +6,6 @@
 package ui
 
 import (
-	"fmt"
 	"io"
 	"os"
 
@@ -33,7 +32,6 @@ func realmain(fp io.Reader) error {
 
 func MyMain() error {
 	if len(os.Args) < 2 {
-		fmt.Println("MyMain did not get a file to work with ")
 		return realmain(nil)
 	}
 	fname := os.Args[1]
diff --git a/ui/output_tab.go b/ui/output_tab.go
index 1ab7718..b0349eb 100644
--- a/ui/output_tab.go
+++ b/ui/output_tab.go
@@ -24,21 +24,6 @@ import (
 	"fyne.io/fyne/v2/widget"
 )
 
-// We have a channel that sends the status to the output tab.
-type status uint8
-
-const (
-	calculating  status = iota // currently running a simulation
-	resultsReady               // You should collect the results
-	errorCalc                  // You should display an error message
-)
-
-type workstatus struct {
-	fdata, xdata []byte // the data in a function or X value plot
-	err          error
-	status       status // Tells us what we should do now
-}
-
 // If we get a message with an error, display it
 func showErrTab(cntr *fyne.Container, err error) {
 	cntr.Objects = nil
@@ -84,9 +69,13 @@ func innerWrite(d []byte, parent fyne.Window) {
 var leftText = `Stretch window and play with the divider to make plots bigger.
 Clicking on a button below will let one save the plot of the cost function or the
 actual X values given to the function`
-type spacer struct { widget.Separator }
-func (spacer) Hide () {}
-func (spacer) MinSize () fyne.Size{ return fyne.Size{Width:1, Height:2}}
+
+// This spacer is the simplest way to stick a bit of room in the stucture
+// below. We cheat a bit in that the Hide() is just a noop.
+type spacer struct{widget.Separator }
+
+func (spacer) Hide()              {}
+func (spacer) MinSize() fyne.Size { return fyne.Size{Width: 1, Height: 2} }
 
 // leftbar sets up the buttons on the left
 func leftbar(win fyne.Window, fdata, xdata []byte) *fyne.Container {
@@ -98,8 +87,8 @@ func leftbar(win fyne.Window, fdata, xdata []byte) *fyne.Container {
 	infobox := widget.NewLabel(leftText)
 	infobox.Alignment = fyne.TextAlignLeading
 	infobox.Wrapping = fyne.TextWrapWord
-	s:= &spacer{}
-	return container.NewVBox(infobox, s, fdataBtn, s,xdataBtn)
+	s := &spacer{}
+	return container.NewVBox(infobox, s, fdataBtn, s, xdataBtn)
 }
 
 // png2image takes the file data we have and puts it in a fyne image with
@@ -115,24 +104,25 @@ func png2image(d []byte, fname string) *canvas.Image {
 // This allows us to make a layout for putting two plots on top of each other.
 // The object resize()'s so that it fills the width and each object (plot)
 // gets half of the vertical space. An rbox satisfies fyne's Layout interface.
-type rbox struct {}
+type rbox struct{}
 
 // MinSize for an rbox... The width of the two plots, and height for two of them.
 func (*rbox) MinSize(objects []fyne.CanvasObject) fyne.Size {
-	w:= objects[0].MinSize().Width
-	h:= objects[0].MinSize().Height
-	return (fyne.NewSize(w, 2.0 *h))
+	w := objects[0].MinSize().Width
+	h := objects[0].MinSize().Height
+	return (fyne.NewSize(w, 2.0*h))
 }
+
 // Layout for an rbox. Put two objects on top of each other in a box that resizes.
 func (*rbox) Layout(objects []fyne.CanvasObject, cntrSize fyne.Size) {
 	top := objects[0]
 	bottom := objects[1]
 	half := cntrSize.Height / 2.
 	sWant := fyne.Size{Width: cntrSize.Width, Height: half}
-	top.Resize (sWant)
-	bottom.Resize (sWant)
-	top.Move (fyne.Position{0,0})
-	bottom.Move (fyne.Position{0, half})
+	top.Resize(sWant)
+	bottom.Resize(sWant)
+	top.Move(fyne.Position{X: 0, Y: 0})
+	bottom.Move(fyne.Position{X: 0, Y: half})
 }
 
 // showResultsTab shows buttons on the left and two plots on the right
@@ -144,8 +134,8 @@ func showResultsTab(cntr *fyne.Container, win fyne.Window, fdata, xdata []byte)
 	left := leftbar(win, fdata, xdata)
 
 	split := container.NewHSplit(left, right)
-	split.SetOffset (0.01)
-	cntr.Add (split)
+	split.SetOffset(0.01)
+	cntr.Add(split)
 }
 
 // outputTab is run as a background process. After showing the initial
@@ -158,7 +148,7 @@ func outputTab(genParams genParams, cntr *fyne.Container, form *widget.Form) {
 		case calculating:
 			showCalcTab(cntr)
 		case resultsReady:
-			showResultsTab(cntr, genParams.win, s.fdata, s.xdata)
+			showResultsTab(cntr, genParams.win, s.Fdata, s.Xdata)
 			form.Enable()
 			form.Refresh()
 		case errorCalc:
diff --git a/ui/ui_run.go b/ui/ui_run.go
index b1ea8a8..dfa4be5 100644
--- a/ui/ui_run.go
+++ b/ui/ui_run.go
@@ -22,6 +22,22 @@ type genParams struct{
 	chn chan workstatus  // To tell the output tab current status
 }
 
+// We have a channel that sends the status to the output tab.
+type status uint8
+
+const (
+	calculating  status = iota // currently running a simulation
+	resultsReady               // You should collect the results
+	errorCalc                  // You should display an error message
+)
+
+type workstatus struct {
+	mcwork.MCresult
+//	fdata, xdata []byte // the data in a function or X value plot
+	err          error
+	status       status // Tells us what we should do now
+}
+
 // UiDoRun sets up the screen to do a run. It is effectively a big
 // wrapper around the DoRun() function.
 func UiDoRun(mcPrm *mcwork.McPrm) error {
-- 
GitLab