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