From ac9a0c8cf2f637c1c2900600706e7b8bd49b81b2 Mon Sep 17 00:00:00 2001 From: "Andrew E. Torda" <torda@zbh.uni-hamburg.de> Date: Fri, 25 Feb 2022 16:48:19 +0100 Subject: [PATCH] Added a switch to turn off adaptive move size. --- TODO | 8 +++----- doc.go | 1 + mc_work/dorun.go | 47 +++++++++++++++++++++++++++------------------- mc_work/mc_work.go | 3 ++- mc_work/rdprm.go | 4 ++++ ui/input_tab.go | 23 +++++++++++++++-------- ui/output_tab.go | 12 ++++++------ ui/ui_run.go | 20 ++++++++++---------- 8 files changed, 69 insertions(+), 49 deletions(-) diff --git a/TODO b/TODO index 81a4483..c7e1ba1 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,5 @@ -- in main MC, add a boolean to make move size adjustment optional. Otherwise the method works too well for an Uebung. - -- Add radio button to input tab for adaptive move sizes - - Change the adaptive scheme so it looks more like weak coupling. Scale the adjustment by current rate / desired rate -- add testing to the ui directory \ No newline at end of file +- add testing to the ui directory + +- Save the best found configuration, print out at end \ No newline at end of file diff --git a/doc.go b/doc.go index a3912c0..2b7977e 100644 --- a/doc.go +++ b/doc.go @@ -7,6 +7,7 @@ // where input_file is a set of name-value pairs. These are defined with defaults // in rdprm.go. At the moment, we have package main + // BUGS // A chart in png format from go-chart is no problem, but a file in svg format causes a parse error in fyne. /* diff --git a/mc_work/dorun.go b/mc_work/dorun.go index 042d982..0ad6d92 100644 --- a/mc_work/dorun.go +++ b/mc_work/dorun.go @@ -189,10 +189,11 @@ func nRunAdj(mcPrm *McPrm, nstep int, runAcc float64, xDlta float64) float64 { return xDlta } -// doRun does a Monte Carlo run. Although single precision is fine for the -// 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. +// doRun does a Monte Carlo run. +// We only need single precision for most things, except for function +// values, but the interface to the plot and interface packages mostly +// want double precision, so we have a lot of float64 that shoudl be +// float32. func DoRun(mcPrm *McPrm) (MCresult, error) { var cprm cprm broken := MCresult{} @@ -204,8 +205,11 @@ func DoRun(mcPrm *McPrm) (MCresult, error) { defer cprm.fOut.Flush() } - if c, ok := cprm.fplotWrt.(io.Closer); ok { // will also have to do for X values - defer c.Close() + if c, ok := cprm.fplotWrt.(io.Closer); ok { + defer c.Close() // file handle for function values + } + if c, ok := cprm.xplotWrt.(io.Closer); ok { + defer c.Close() // file handle for X trajectories } var nAcc int // Counter, number of accepted moves @@ -213,32 +217,33 @@ func DoRun(mcPrm *McPrm) (MCresult, error) { xT := make([]float32, len(mcPrm.XIni)) // trial position runAcc := accRateIdeal // Running acceptance rate, exponentially weighted - for i := range mcPrm.XIni { - x[i] = float32(mcPrm.XIni[i]) + for i := range mcPrm.XIni { // Initial coordinates from + x[i] = float32(mcPrm.XIni[i]) // the control structure } - // copy(x, mcPrm.XIni) I had to replace this with the loop above fOld := ackley.Ackley(x) // Initial function value fTrial := fOld tmprtr := float64(mcPrm.IniTmp) nRunAcc := nRunAccAdj // Every nRunAccAdj, try adjusting the step size. const runMult = 0.99 - xDlta := mcPrm.XDlta // Adaptable step size + xDlta := mcPrm.XDlta // Step size which might be adjusted on the fly saveStep(&cprm, 0, tmprtr, x, fOld) for n := 0; n < mcPrm.NStep; n++ { var accept bool - nRunAcc-- - if nRunAcc == 0 { // Do we want to try adjusting the step size ? - xDlta = nRunAdj(mcPrm, n, runAcc, xDlta) - nRunAcc = nRunAccAdj // Reset the counter + if mcPrm.AdaptStep { + nRunAcc-- + if nRunAcc == 0 { // Do we want to try adjusting the step size ? + xDlta = nRunAdj(mcPrm, n, runAcc, xDlta) + nRunAcc = nRunAccAdj // Reset the counter + } } newx(x, xT, cprm.rand, xDlta) fTrial = ackley.Ackley(xT) if fTrial <= fOld { accept = true } else { - delta := fTrial - fOld // Make the decision as to whether to + delta := fTrial - fOld // Decision as to whether to t := math.Exp(-delta / tmprtr) // accept or reject the trial if cprm.rand.Float64() < t { accept = true @@ -246,12 +251,16 @@ func DoRun(mcPrm *McPrm) (MCresult, error) { } if accept { nAcc++ - runAcc = runMult*runAcc + (1.0 - runMult) copy(x, xT) fOld = fTrial saveStep(&cprm, n+1, tmprtr, x, fTrial) - } else { // update the running estimate of acceptance - runAcc = runMult * runAcc + if mcPrm.AdaptStep { + runAcc = runMult*runAcc + (1.0 - runMult) + } + } else { // else don't really have to do anything, but update statistics + if mcPrm.AdaptStep { + runAcc = runMult * runAcc // update the running estimate of acceptance + } } if cprm.coolme { tmprtr *= cprm.coolMult @@ -279,5 +288,5 @@ func DoRun(mcPrm *McPrm) (MCresult, error) { xdata = xBuf.Bytes() } - return MCresult{ Fdata: fdata, Xdata: xdata, NStep: mcPrm.NStep, NAcc: nAcc}, 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 c3dada8..2942105 100644 --- a/mc_work/mc_work.go +++ b/mc_work/mc_work.go @@ -15,6 +15,7 @@ type McPrm struct { fOutName string // where we write output to fPltName string // where we plot to (function values) xPltName string // where we plot x trajectories to + AdaptStep bool // Adaptive step sizes verbose bool dummy bool // for testing. If set, do not do a run } @@ -26,5 +27,5 @@ var Seed int // 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 + NStep, NAcc int // number of steps and accepted steps } diff --git a/mc_work/rdprm.go b/mc_work/rdprm.go index d6cac58..738cd21 100644 --- a/mc_work/rdprm.go +++ b/mc_work/rdprm.go @@ -29,6 +29,7 @@ var cmdDflt = []struct { {"foutname", ""}, {"fpltname", ""}, // empty means no plots of function {"xpltname", ""}, + {"adaptstep", ""}, {"verbose", ""}, {"dummy", ""}, } @@ -88,6 +89,9 @@ func digest(prmMap map[string]string, mcPrm *McPrm) error { if prmMap["verbose"] != "" { mcPrm.verbose = true } + if prmMap["adaptstep"] != "" { + mcPrm.AdaptStep = true + } if prmMap["dummy"] != "" { mcPrm.dummy = true } diff --git a/ui/input_tab.go b/ui/input_tab.go index bd934ce..8625f90 100644 --- a/ui/input_tab.go +++ b/ui/input_tab.go @@ -11,7 +11,7 @@ import ( "os" "strconv" "strings" - + "fyne.io/fyne/v2" "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/dialog" @@ -86,6 +86,7 @@ func intItem(xAddr *int, label string, htext string) (*widget.FormItem, func()) } 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 @@ -114,7 +115,12 @@ func paramForm(genParams genParams) *widget.Form { 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") - + + adaptbind := binding.BindBool(&mcPrm.AdaptStep) + adaptEntry := widget.NewCheckWithData("adaptive step size", adaptbind) + adaptItem := widget.NewFormItem("fI string", adaptEntry) + adaptItem.HintText = "Turn on adaptive moves to try to keep acceptance near 5%" + adaptReload := func() { _ = adaptbind.Reload() } reloadPTab := func() { iniTmpReload() fnlTmpReload() @@ -122,6 +128,7 @@ func paramForm(genParams genParams) *widget.Form { xIniEntry.SetText(fslicestrng(mcPrm.XIni)) nStepReload() seedReload() + adaptReload() } rdfile := func() { rdwork(genParams, reloadPTab) } @@ -130,7 +137,7 @@ func paramForm(genParams genParams) *widget.Form { r := widget.NewForm( // Lays out the items in the form iniTmpItem, fnlTmpItem, xDltaItem, xIniItem, - nStepItem, seedItem, rdFileItem) + nStepItem, seedItem, adaptItem, 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 @@ -139,8 +146,8 @@ func paramForm(genParams genParams) *widget.Form { // getmcprm gets MC parameters from a file. It is called by the file open // dialog and is given an already open file handle. -func getmcprm (rd fyne.URIReadCloser, err error, genParams genParams, reload func()) { - win :=genParams.win +func getmcprm(rd fyne.URIReadCloser, err error, genParams genParams, reload func()) { + win := genParams.win mcPrm := genParams.mcPrm if err != nil { dialog.NewError(err, win).Show() @@ -163,8 +170,8 @@ func getmcprm (rd fyne.URIReadCloser, err error, genParams genParams, reload fu // rdwork is called after the file open dialog gets "OK" //func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, reload func()) { func rdwork(genParams genParams, reload func()) { - getter := func (rd fyne.URIReadCloser, err error) { - getmcprm (rd, err, genParams, reload) + getter := func(rd fyne.URIReadCloser, err error) { + getmcprm(rd, err, genParams, reload) } t := dialog.NewFileOpen(getter, genParams.win) @@ -186,7 +193,7 @@ func mcWrap(genParams genParams) { dialog.NewError(err, genParams.win).Show() chn <- workstatus{status: errorCalc, err: err} } else { - chn <- workstatus{ MCresult: result, status: resultsReady} + chn <- workstatus{MCresult: result, status: resultsReady} } } diff --git a/ui/output_tab.go b/ui/output_tab.go index ddf1a4b..a0eaf74 100644 --- a/ui/output_tab.go +++ b/ui/output_tab.go @@ -27,10 +27,10 @@ import ( ) // runStatTxt should summarise some of the statistics -func runStatTxt (rslt * mcwork.MCresult) fyne.Widget { - txt := fmt.Sprintf ("Num steps %d\nNum accepted %d\nacceptance rate %.1f %%", - rslt.NStep, rslt.NAcc, (float32(rslt.NAcc)/float32(rslt.NStep)) * 100.) - r := widget.NewLabel( txt) +func runStatTxt(rslt *mcwork.MCresult) fyne.Widget { + txt := fmt.Sprintf("Num steps %d\nNum accepted %d\nacceptance rate %.1f %%", + rslt.NStep, rslt.NAcc, (float32(rslt.NAcc)/float32(rslt.NStep))*100.) + r := widget.NewLabel(txt) return r } @@ -82,7 +82,7 @@ actual X values given to the function` // 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 } +type spacer struct{ widget.Separator } func (spacer) Hide() {} func (spacer) MinSize() fyne.Size { return fyne.Size{Width: 1, Height: 2} } @@ -141,7 +141,7 @@ func showResultsTab(cntr *fyne.Container, win fyne.Window, rslt mcwork.MCresult) fImage := png2image(rslt.Fdata, "function.png") xImage := png2image(rslt.Xdata, "xdata.png") rbox := container.New(&rbox{}, fImage, xImage) - right := container.NewVSplit (rbox, runStatTxt(&rslt)) + right := container.NewVSplit(rbox, runStatTxt(&rslt)) right.SetOffset(0.99) left := leftbar(win, rslt.Fdata, rslt.Xdata) diff --git a/ui/ui_run.go b/ui/ui_run.go index dfa4be5..1360bd2 100644 --- a/ui/ui_run.go +++ b/ui/ui_run.go @@ -16,10 +16,10 @@ import ( ) // Convenience struct so we do not have perverse long parameter lists -type genParams struct{ - mcPrm *mcwork.McPrm // The MC parameters shared with the main simulation - win fyne.Window // Parent window. Needed for all dialog boxes - chn chan workstatus // To tell the output tab current status +type genParams struct { + mcPrm *mcwork.McPrm // The MC parameters shared with the main simulation + win fyne.Window // Parent window. Needed for all dialog boxes + chn chan workstatus // To tell the output tab current status } // We have a channel that sends the status to the output tab. @@ -33,9 +33,9 @@ const ( 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 + // 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 @@ -45,7 +45,7 @@ func UiDoRun(mcPrm *mcwork.McPrm) error { win := a.NewWindow("Monte Carlo") chn := make(chan workstatus) defer close(chn) - var genParams = genParams {mcPrm, win, chn} + var genParams = genParams{mcPrm, win, chn} inputForm := inputTab(genParams) quitbutton := widget.NewButton("click somewhere in here to confirm", a.Quit) cntrOut := container.NewMax() @@ -59,5 +59,5 @@ func UiDoRun(mcPrm *mcwork.McPrm) error { return nil } -func nothing(...interface{}){} -func breaker(...interface{}){} +func nothing(...interface{}) {} +func breaker(...interface{}) {} -- GitLab