diff --git a/Makefile b/Makefile index 923682cd392cc70d52d96b5f2a968c17f37fb57d..6268a46a23f8def201413c6bb6e8b9bc5e66d435 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ all: go build ./... +test: + go test ./... + go test -tags no_gfx ./... + clean: go clean rm -rf */*_delme.* diff --git a/go.sum b/go.sum index 95dec1be5bb6e11aba3379fbf019c64f7f486701..a90edb9dd2eaed553195e95de6463f6e301ba775 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -fyne.io/fyne v1.4.3 h1:356CnXCiYrrfaLGsB7qLK3c6ktzyh8WR05v/2RBu51I= -fyne.io/fyne v1.4.3/go.mod h1:8kiPBNSDmuplxs9WnKCkaWYqbcXFy0DeAzwa6PBO9Z8= fyne.io/fyne/v2 v2.1.2 h1:avp9CvLAUdvE7fDMtH1tVKyjxEWHWcpow6aI6L7Kvvw= fyne.io/fyne/v2 v2.1.2/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -14,15 +12,11 @@ github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8 github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= -github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be h1:Z28GdQBfKOL8tNHjvaDn3wHDO7AzTRkmAXvHvnopp98= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= @@ -32,10 +26,11 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -56,7 +51,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I= github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.8 h1:Nw158Q8QN+CPgTmVRByhVwapp8Mm1e2blinhmx4wx5E= github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -64,27 +58,20 @@ gitlab.rrz.uni-hamburg.de/Bae5157/axticks v0.0.0-20220120103412-d663ebb46145 h1: gitlab.rrz.uni-hamburg.de/Bae5157/axticks v0.0.0-20220120103412-d663ebb46145/go.mod h1:5CNHyqeidRypmIVTnQksGqnmP56Oshw1Zv1nlUezrpQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -92,19 +79,16 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main_gfx.go b/main_gfx.go new file mode 100644 index 0000000000000000000000000000000000000000..db7a65b050465814e25d4765b7a163bc0f76b9f0 --- /dev/null +++ b/main_gfx.go @@ -0,0 +1,30 @@ +// Aug 2021 +// +build !no_gfx + +// Ackley_mc is for playing with Monte Carlo or simulated annealing on the +// ackley function in N dimensions. +// +// ackley_mc input_file +// where input_file has a list of keywords and values. + +package main + +import ( + "fmt" + "os" + + "example.com/ackley_mc/ui" +) + +const ( + exitSuccess = iota + exitFailure +) + +func main() { + if err := ui.MyMain(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(exitFailure) + } + os.Exit(exitSuccess) +} diff --git a/main.go b/main_nogfx.go similarity index 87% rename from main.go rename to main_nogfx.go index e69a7cb2973720296bc6e7df00d1aa6cbb6c1355..adae62813be58bb47706f7b4d2dea1343f7ff358 100644 --- a/main.go +++ b/main_nogfx.go @@ -1,4 +1,5 @@ // Aug 2021 +// +build no_gfx // Ackley_mc is for playing with Monte Carlo or simulated annealing on the // ackley function in N dimensions. @@ -21,7 +22,7 @@ const ( ) func main() { - if err := mc_work.MyMain(); err != nil { + if err := mcwork.MyMain(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(exitFailure) } diff --git a/mc_work/dorun.go b/mc_work/dorun.go index 78c0552e9f32af23d4fbbd1fbd8da3a0c00078d1..4c05d432cf70364087dfffaf086c65fa3aff183f 100644 --- a/mc_work/dorun.go +++ b/mc_work/dorun.go @@ -1,5 +1,5 @@ // Aug 2021 -package mc_work +package mcwork import ( "bufio" @@ -12,7 +12,6 @@ import ( "sync" "example.com/ackley_mc/ackley" - "example.com/ackley_mc/ui" ) const ( @@ -54,35 +53,35 @@ type cprm struct { // parameters calculated from input } // isNotSane checks for obviously silly parameters -func isNotSane(mcPrm *mcPrm) error { +func isNotSane(mcPrm *McPrm) error { if mcPrm.fOutName == "" && mcPrm.fPltName == "" && mcPrm.xPltName == "" { if false { // if there are no graphics compiled in, .. return fmt.Errorf("All output files are empty. You would not see anything") } } - if mcPrm.iniTmp < 0 { - return fmt.Errorf("negative initial temperature %g", mcPrm.iniTmp) + if mcPrm.IniTmp < 0 { + return fmt.Errorf("negative initial temperature %g", mcPrm.IniTmp) } return nil } // setupRun does things like get the cooling rate, seed the random numbers. -func setupRun(mcPrm *mcPrm, cprm *cprm) error { +func setupRun(mcPrm *McPrm, cprm *cprm) error { var err error if mcPrm.dummy { return nil } cprm.rand = rand.New(rand.NewSource(getSeed())) - if mcPrm.iniTmp != mcPrm.fnlTmp { // cooling or constant temperature ? + if mcPrm.IniTmp != mcPrm.FnlTmp { // cooling or constant temperature ? var coolrate float64 cprm.coolme = true - if mcPrm.fnlTmp > mcPrm.iniTmp { + if mcPrm.FnlTmp > mcPrm.IniTmp { const finalTempErr = "Final temp %g higher than initial temp %g" - return fmt.Errorf(finalTempErr, mcPrm.fnlTmp, mcPrm.iniTmp) + return fmt.Errorf(finalTempErr, mcPrm.FnlTmp, mcPrm.IniTmp) } - nStep := float64(mcPrm.nStep) - fnlTmp, iniTmp := float64(mcPrm.fnlTmp), float64(mcPrm.iniTmp) + nStep := float64(mcPrm.NStep) + fnlTmp, iniTmp := float64(mcPrm.FnlTmp), float64(mcPrm.IniTmp) if fnlTmp == 0 { // avoid divide by zeroes fnlTmp = math.SmallestNonzeroFloat32 } @@ -112,7 +111,7 @@ func setupRun(mcPrm *mcPrm, cprm *cprm) error { var w bytes.Buffer // could be fancy, preallocate and use bytes.Newbuffer cprm.fplotWrt = &w } - n_alloc := mcPrm.nStep / 5 // About a fifth of the number of steps + n_alloc := mcPrm.NStep / 5 // About a fifth of the number of steps cprm.plotnstp = make([]float64, 0, n_alloc) cprm.plotf = make([]float64, 0, n_alloc) if cprm.coolme { @@ -130,7 +129,7 @@ func setupRun(mcPrm *mcPrm, cprm *cprm) error { var w bytes.Buffer cprm.xplotWrt = &w // I think we can remove the preceding line } - cprm.plotXtrj = make([]float32, 0, int(n_alloc)*len(mcPrm.xIni)) + cprm.plotXtrj = make([]float32, 0, int(n_alloc)*len(mcPrm.XIni)) if err = isNotSane(mcPrm); err != nil { return err } @@ -148,14 +147,14 @@ func alt_newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float32) { } // newx will move just one coordinate at a time -func newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float32) { +func newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float64) { var iDim int if (len(xold)) > 1 { iDim = rand.Intn(len(xold)) // pick one dimension to change } - t := 2.*rand.Float32() - 1 + t := 2.*rand.Float64() - 1 t *= xDlta - xT[iDim] = xold[iDim] + t + xT[iDim] = xold[iDim] + float32(t) } // printfVal is the loop to print out the function value and coordinates @@ -185,7 +184,7 @@ func saveStep(cprm *cprm, n uint32, tmprtr float64, x []float32, fTrial float64) } // nRunAdj will try to adjust the step size, given our history of accept/reject. -func nRunAdj(mcPrm *mcPrm, nstep uint32, runAcc float64, xDlta float32) float32 { +func nRunAdj(mcPrm *McPrm, nstep uint32, runAcc float64, xDlta float64) float64 { const step = "step" if runAcc < accRateIdeal-0.01 { // acceptance rate too low xDlta *= 0.9 @@ -205,10 +204,10 @@ func nRunAdj(mcPrm *mcPrm, nstep uint32, runAcc float64, xDlta float32) float32 // 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) error { +func DoRun(mcPrm *McPrm) ([]byte, []byte, error) { var cprm cprm if err := setupRun(mcPrm, &cprm); err != nil { - return err + return nil, nil, err } if cprm.doTxtFval { // The text file documenting the run defer cprm.fOutRaw.Close() @@ -220,19 +219,24 @@ func doRun(mcPrm *mcPrm) error { } var nAcc int // Counter, number of accepted moves - x := make([]float32, len(mcPrm.xIni)) // current position - xT := make([]float32, len(mcPrm.xIni)) // trial position + x := make([]float32, len(mcPrm.XIni)) // current position + xT := make([]float32, len(mcPrm.XIni)) // trial position runAcc := accRateIdeal // Running acceptance rate, exponentially weighted - copy(x, mcPrm.xIni) + + for i := range mcPrm.XIni { + x[i] = float32(mcPrm.XIni[i]) + } + + // 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) + 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 // Adaptable step size saveStep(&cprm, 0, tmprtr, x, fOld) - for n := uint32(0); n < mcPrm.nStep; n++ { + for n := uint32(0); n < mcPrm.NStep; n++ { var accept bool nRunAcc-- if nRunAcc == 0 { // Do we want to try adjusting the step size ? @@ -266,32 +270,25 @@ func doRun(mcPrm *mcPrm) error { // On plots, we want the last step, even if nothing changed. if fTrial != fOld { // last move was not accepted, so we have to add last step - saveStep(&cprm, mcPrm.nStep, tmprtr, x, fOld) + 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+1) if err := plotfWrt(&cprm); err != nil { - return err + return nil, nil, err } - if err := plotxWrt(&cprm, len(mcPrm.xIni)); err != nil { - return err - } - { - var fdata, xdata []byte - if funcBuf, ok := cprm.fplotWrt.(withBytes); ok { - fdata = funcBuf.Bytes() - } - if xBuf, ok := cprm.xplotWrt.(withBytes); ok { - xdata = xBuf.Bytes() - } - nothing(fdata, xdata) - ui.Scrnplt(fdata, xdata) - nothing(ui.Scrnplt) + if err := plotxWrt(&cprm, len(mcPrm.XIni)); err != nil { + return nil, nil, err } - /* if bBuf, := plotxWrt(&cprm, mcPrm.xPltName, len(mcPrm.xIni)); err != nil { - return err - } */ + var fdata, xdata []byte + if funcBuf, ok := cprm.fplotWrt.(withBytes); ok { + fdata = funcBuf.Bytes() + } + if xBuf, ok := cprm.xplotWrt.(withBytes); ok { + xdata = xBuf.Bytes() + } + nothing(fdata, xdata) - return nil + return fdata, xdata, nil } diff --git a/mc_work/export_test.go b/mc_work/export_test.go index 2d4bddfbf37924d3cde6af9f6b2ab5017deda45b..f8d71022507b808278c61b39369379fd70e06232 100644 --- a/mc_work/export_test.go +++ b/mc_work/export_test.go @@ -1,4 +1,4 @@ -package mc_work +package mcwork var SetSuffix = setSuffix var RemoveQuotes = removeQuotes diff --git a/mc_work/mc_work.go b/mc_work/mc_work.go index 7b65f85cf1a018eecdd4123734af5b813dedf902..476888ad621ab142bc8f2448cf2cd2ecb50cd47b 100644 --- a/mc_work/mc_work.go +++ b/mc_work/mc_work.go @@ -1,22 +1,20 @@ // Aug 2021 -package mc_work +package mcwork import ( "flag" "fmt" - "io" "os" - - "example.com/ackley_mc/ackley" ) -type mcPrm struct { - iniTmp, fnlTmp float32 // Initial and final temperatures - xIni []float32 // initial x slice - xDlta float32 // change x by up to +- xDlta in trial moves - nStep uint32 // number of steps - nRun uint32 // number of separate runs +// Have to export everything so the graphical interface can get to them +type McPrm struct { + IniTmp, FnlTmp float64 // Initial and final temperatures + XIni []float32 // initial x slice + XDlta float64 // change x by up to +- xDlta in trial moves + NStep uint32 // number of steps + NRun uint32 // number of separate runs fOutName string // where we write output to fPltName string // where we plot to (function values) xPltName string // where we plot x trajectories to @@ -31,39 +29,3 @@ func usage() { u := `[options] input_parameter_file` fmt.Fprintf(flag.CommandLine.Output(), "usage of %s: %s\n", os.Args[0], u) } - -// 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. -func realmain(fp io.Reader) error { - var mcPrm mcPrm - if err := rdPrm(fp, &mcPrm); err != nil { - return err - } - if err := doRun(&mcPrm); err != nil { - return err - } - var x [1]float32 - ackley.Ackley(x[:]) - return nil -} - -// 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. -func MyMain() error { - if len(os.Args) < 2 { - usage() - return fmt.Errorf("No input file given") - } - fname := os.Args[1] - if fp, err := os.Open(fname); err != nil { - return err - } else { - defer fp.Close() - if err := realmain(fp); err != nil { - return err - } - } - return nil -} diff --git a/mc_work/mc_work_test.go b/mc_work/mc_work_test.go index 485c92393e02ac4e0fb5bbd036971665d991e4dc..0a067f9f085b93f22c82c0907a46397ff86a2818 100644 --- a/mc_work/mc_work_test.go +++ b/mc_work/mc_work_test.go @@ -1,6 +1,7 @@ // Aug 2021 +// +build no_gfx -package mc_work +package mcwork import ( "fmt" diff --git a/mc_work/plot.go b/mc_work/plot.go index e8268846bee4113de7a83d2b689fc26bfea2a6cf..e2112608b61cf6df3a368f03f73992bd9619b619 100644 --- a/mc_work/plot.go +++ b/mc_work/plot.go @@ -15,7 +15,7 @@ // the X-axis differently. The solution is that we draw the temperature on both // plots, but set all the style attributes to be invisible when we plot the // X trajectories. -package mc_work +package mcwork import ( "fmt" diff --git a/mc_work/rdprm.go b/mc_work/rdprm.go index 3730db7edeb6ba0401d33be6a9667f44d576273e..e3b8ad2619f65ad3b1ca6c477a713233d07660f8 100644 --- a/mc_work/rdprm.go +++ b/mc_work/rdprm.go @@ -4,7 +4,7 @@ // These are stored in a map along with string values. These are then converted // to floats or ints and put into a structure or variable. -package mc_work +package mcwork import ( "bufio" @@ -34,15 +34,23 @@ var cmdDflt = []struct { } // digest digests the map, fills out our structure and sets the seed. -func digest(prmMap map[string]string, mcPrm *mcPrm) error { +func digest(prmMap map[string]string, mcPrm *McPrm) error { var err error - getf := func(s string) float32 { +/* getf := func(s string) float32 { if err != nil { return 0.0 } var r float64 r, err = strconv.ParseFloat(s, 32) return float32(r) + } */ + getf64 := func(s string) float64 { + if err != nil { + return 0.0 + } + var r float64 + r, err = strconv.ParseFloat(s, 64) + return r } getu := func(s string) uint32 { if err != nil { @@ -72,12 +80,12 @@ func digest(prmMap map[string]string, mcPrm *mcPrm) error { } return x } - mcPrm.iniTmp = getf(prmMap["ini_temp"]) - mcPrm.fnlTmp = getf(prmMap["final_temp"]) - mcPrm.xDlta = getf(prmMap["x_delta"]) - mcPrm.nStep = getu(prmMap["n_step"]) - mcPrm.nRun = getu(prmMap["n_run"]) - mcPrm.xIni = getx(prmMap["x_ini"]) + mcPrm.IniTmp = getf64(prmMap["ini_temp"]) + mcPrm.FnlTmp = getf64(prmMap["final_temp"]) + mcPrm.XDlta = getf64(prmMap["x_delta"]) + mcPrm.NStep = getu(prmMap["n_step"]) + mcPrm.NRun = getu(prmMap["n_run"]) + mcPrm.XIni = getx(prmMap["x_ini"]) mcPrm.fOutName = prmMap["foutname"] mcPrm.fPltName = prmMap["fpltname"] mcPrm.xPltName = prmMap["xpltname"] @@ -106,8 +114,8 @@ pairs. Values for x_ini are separated by commas without spaces. return s } -// rdPrmInner is in a function by itself, to make testing easier -func rdPrm(fp io.Reader, mcPrm *mcPrm) error { +// RdPrm reads parameters from a io reader and fills out an control structure. +func RdPrm(fp io.Reader, mcPrm *McPrm) error { prmMap := make(map[string]string) for _, s := range cmdDflt { prmMap[s.key] = s.v // Put default values into map diff --git a/mc_work/realmain_nogfx.go b/mc_work/realmain_nogfx.go new file mode 100644 index 0000000000000000000000000000000000000000..a63116ed07f5df35dcda0a070bc6a990b4cc9462 --- /dev/null +++ b/mc_work/realmain_nogfx.go @@ -0,0 +1,48 @@ +// feb 2022 +// We are building a version without graphics +// +build no_gfx + + +// This should mirror the file realmain_gfx.go. It should also remain short. +package mcwork + +import ( + "io" + "os" + "fmt" +) + +// 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. +func realmain(fp io.Reader) error { + var mcPrm McPrm + if err := RdPrm(fp, &mcPrm); err != nil { + return err + } + if _, _, err := doRun(&mcPrm); err != nil { + return err + } + + return nil +} + +// 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. +func MyMain() error { + if len(os.Args) < 2 { + usage() + return fmt.Errorf("No input file given") + } + fname := os.Args[1] + if fp, err := os.Open(fname); err != nil { + return err + } else { + defer fp.Close() + if err := realmain(fp); err != nil { + return err + } + } + return nil +} diff --git a/mc_work/set_suffix.go b/mc_work/set_suffix.go index c0bf8d0b70a0fe3267a3ed24ddc70dd691a00576..5428c204779cf6ffe5f8722c681f0e5ddff3cc9e 100644 --- a/mc_work/set_suffix.go +++ b/mc_work/set_suffix.go @@ -1,5 +1,5 @@ // a utility -package mc_work +package mcwork import ( "errors" diff --git a/mc_work/unwanted.goat b/mc_work/unwanted.goat deleted file mode 100644 index cf1601145ce8b74fd4fea4219d56b337ea0b3d11..0000000000000000000000000000000000000000 --- a/mc_work/unwanted.goat +++ /dev/null @@ -1,16 +0,0 @@ -// maketicks this version works, but usually leaves you wanting one tick more. -/*func alt_maketicks(axisDscrpt axticks.AxisDscrpt, prcsn int) []chart.Tick { - xmin, xmax, delta, prcsn := axisDscrpt.Xmin, axisDscrpt.Xmax, axisDscrpt.Delta, axisDscrpt.Prcsn - var t []chart.Tick - const fstr = "%.*f" - xv, xl := xmin, fmt.Sprintf(fstr, prcsn, xmin) - if xmin > 3.56 && xmin < 3.58 { - fmt.Println (axisDscrpt) - } - for ; xv <= xmax; xv, xl = xv+delta, fmt.Sprintf("%.*f", prcsn, xv) { - t = append(t, chart.Tick {Value: xv, Label: xl}) - } - - return t -}*/ - diff --git a/ui/embed_exmple.go b/ui/embed_exmple.go new file mode 100644 index 0000000000000000000000000000000000000000..44342ac029206dd7273f672883440c8774b66dc9 --- /dev/null +++ b/ui/embed_exmple.go @@ -0,0 +1,20 @@ +// 15 Feb 2022 +// Let us read up an example input file and put its contents into a McPrm +// (monte carlo control parameters) structure. + +package ui + +import ( + _ "embed" + "strings" + + "example.com/ackley_mc/mc_work" +) + +//go:embed example_input +var exmplInput string + +func getExmpl(mcPrm *mcwork.McPrm) error { + s := strings.NewReader(exmplInput) + return mcwork.RdPrm(s, mcPrm) +} diff --git a/example_input b/ui/example_input similarity index 100% rename from example_input rename to ui/example_input diff --git a/ui/mymain.go b/ui/mymain.go new file mode 100644 index 0000000000000000000000000000000000000000..27bc825e3decb2a557b8d1bdec6a7c9188d6ffeb --- /dev/null +++ b/ui/mymain.go @@ -0,0 +1,45 @@ +// feb 2022 +// We are building a version without graphics +// +build !no_gfx + +package ui + +import ( + "fmt" + "io" + "os" + + "example.com/ackley_mc/mc_work" +) + +// 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. +func realmain(fp io.Reader) error { + var mcPrm mcwork.McPrm + if fp != nil { + if err := mcwork.RdPrm(fp, &mcPrm); err != nil { + return err + } + } else { + if err := getExmpl(&mcPrm); err != nil { + return err + } + } + return UiDoRun(&mcPrm) + +} + +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] + if fp, err := os.Open(fname); err != nil { + return err + } else { + defer fp.Close() + return realmain(fp) + } +} diff --git a/ui/param_tab.go b/ui/param_tab.go new file mode 100644 index 0000000000000000000000000000000000000000..504f283e494762ea16f6cfb3bbff1da48b83cf16 --- /dev/null +++ b/ui/param_tab.go @@ -0,0 +1,182 @@ +// Feb 2022 +// Set up a tab for getting Monte Carlo Parameters +// This should return an object that can be embedded in a tabbed page. +//go:build !no_gfx +// +build !no_gfx + +package ui + +import ( + "fmt" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/data/binding" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/storage" + "fyne.io/fyne/v2/widget" + "os" + "strconv" + "strings" + + "example.com/ackley_mc/mc_work" +) + +// fslicestrng makes a nice string from a slice of float32's +func fslicestrng(f []float32) string { + if len(f) == 0 { + return "" + } + s := fmt.Sprintf("%g", f[0]) + for i := 1; i < len(f); i++ { + s += fmt.Sprintf(", %g", f[i]) + } + return s +} + +// str2f32 takes a string and returns a slice of float32's +func s2f32(s string) ([]float32, error) { + y := strings.Split(s, ",") + if len(y) < 1 { + return nil, fmt.Errorf("no fields found") + } + x := make([]float32, len(y)) + for i, s := range y { + if r, err := strconv.ParseFloat(strings.TrimSpace(s), 32); err != nil { + return nil, fmt.Errorf("tried to parse %v %v", s, err) + } else { + x[i] = float32(r) + } + } + return x, nil +} + +func validateXini(s string) error { + fmt.Println("validation called") + _, err := s2f32(s) + return err +} + +// paramScreen sets up a screen full of parameters we can adjust. It returns a the screen +// and a function to be called to refresh it if parameters are changed elsewhere, like +// reading from a file. +func paramScreen(mcPrm *mcwork.McPrm) (*fyne.Container, func()) { + + iniTmp := binding.BindFloat(&mcPrm.IniTmp) + iniTmpEntry := widget.NewEntryWithData(binding.FloatToString(iniTmp)) + iniTmpLabel := widget.NewLabel("initial temp") + + fnlTmp := binding.BindFloat(&mcPrm.FnlTmp) + fnlTmpEntry := widget.NewEntryWithData(binding.FloatToString(fnlTmp)) + fnlTmpLabel := widget.NewLabel("final temp") + + xDlta := binding.BindFloat(&mcPrm.XDlta) + xDltaEntry := widget.NewEntryWithData(binding.FloatToString(xDlta)) + xDltaLabel := widget.NewLabel("X delta") + + xIniStr := binding.NewString() + xIniStr.Set(fslicestrng(mcPrm.XIni)) + xIniEntry := widget.NewEntryWithData(xIniStr) + xIniEntry.OnChanged = func(s string) { + x, err := s2f32(s) + if err != nil { + xIniEntry.SetValidationError(err) + } else { + mcPrm.XIni = append(mcPrm.XIni[:0], x...) + } + } + xIniEntry.Validator = validateXini + xIniLabel := widget.NewLabel("X ini") + + r := container.NewGridWithColumns(2, + iniTmpLabel, iniTmpEntry, + fnlTmpLabel, fnlTmpEntry, + xDltaLabel, xDltaEntry, + xIniLabel, xIniEntry) + refreshPScreen := func() { + iniTmp.Reload() + fnlTmp.Reload() + xDlta.Reload() + xIniEntry.SetText(fslicestrng(mcPrm.XIni)) + } + return r, refreshPScreen +} + +func checkok(c *fyne.Container, parent fyne.Window) error { + for _, obj := range c.Objects { + if ent, ok := obj.(*widget.Entry); ok { + if err := ent.Validate(); err != nil { + dialog.NewError(err, parent).Show() + return err + } + } + } + return nil +} + +// rdwork is called after the file open dialog gets "OK" +func rdwork(mcPrm *mcwork.McPrm, parent fyne.Window, refreshme func()) error { + var e error + getmcprm := func(rd fyne.URIReadCloser, err error) { + if err != nil { + fmt.Println("error given to getmcprm") + dialog.NewError(err, parent).Show() + e = err + return + } + if rd == nil { // cancelled + return + } + defer rd.Close() + if err := mcwork.RdPrm(rd, mcPrm); err != nil { + e = err + return + } else { + fmt.Println("I think mcprm is", mcPrm) + refreshme() + dialog.NewInformation("OK confirmed", "read file, no errors", parent).Show() + return + } + } + + t := dialog.NewFileOpen(getmcprm, parent) + + if cwd, err := os.Getwd(); err == nil { + if y, err := storage.ListerForURI(storage.NewFileURI(cwd)); err == nil { + t.SetLocation(y) // If there was an error, we just use default location + } + } + + t.Show() // set an error and see if anybody notices + return e +} + +// inputTab sets up the input page. At the top, we have a button to read from +// a file. +func inputTab(mcPrm *mcwork.McPrm, parent fyne.Window, chn chan workstatus) (*fyne.Container, error) { + paramBinding, refreshPscreen := paramScreen(mcPrm) + rdfile := func() { + if err := rdwork(mcPrm, parent, refreshPscreen); err != nil { + dialog.NewError(err, parent).Show() + } + } + startrun := func() { + if err := checkok(paramBinding, parent); err != nil { + dialog.NewError(err, parent).Show() + return + } + fmt.Println("I want to run with paremeters", mcPrm) + chn <- workstatus{status: calculating} + if fdata, xdata, err := mcwork.DoRun(mcPrm); err != nil { + dialog.NewError(err, parent).Show() + close(chn) + return + } else { + chn <- workstatus{fdata: fdata, xdata: xdata, status: resultsReady} + } + } + rdfileBtn := widget.NewButton("read file", rdfile) + runBtn := widget.NewButton("start run", startrun) + buttons := container.NewVBox(rdfileBtn, runBtn) + c := container.NewHBox(buttons, paramBinding) + return c, nil +} diff --git a/ui/scrnplt.go b/ui/scrnplt.go index 715fc16582508183f7c8080c4d5e385a6f55feaf..ac34c13e8ab8404f0830ba957494a7501df278f7 100644 --- a/ui/scrnplt.go +++ b/ui/scrnplt.go @@ -10,7 +10,6 @@ import ( "fmt" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" @@ -31,7 +30,6 @@ func tryfsave(uriw fyne.URIWriteCloser, err error, data []byte, parent fyne.Wind if err := uriw.Close(); err != nil { fmt.Println("error closing") } - fmt.Printf("trydsave called with %+v %+v", uriw, err) } func fsave(parent fyne.Window, fdata []byte, suggested string) { @@ -43,8 +41,6 @@ func fsave(parent fyne.Window, fdata []byte, suggested string) { t.SetFileName(suggested) t.SetFilter(filter) t.Show() - - fmt.Println("dsave called and returning") } func topmenu(parent fyne.Window, fdata, xdata []byte) *fyne.Menu { @@ -70,19 +66,15 @@ func fileSaved(f fyne.URIWriteCloser, w fyne.Window) { func nothing(...interface{}) {} func breaker(...interface{}) {} -func Scrnplt(fdata, xdata []byte) { - a := app.NewWithID("ackley") // Find a way to check if it is happy - w := a.NewWindow("Monte Carlo") - - w.SetMainMenu(fyne.NewMainMenu(topmenu(w, fdata, xdata))) +func scrnplt(fdata, xdata []byte) *fyne.Container { fImage := canvas.NewImageFromReader(bytes.NewReader(fdata), "func.png") fImage.FillMode = canvas.ImageFillContain xImage := canvas.NewImageFromReader(bytes.NewReader(xdata), "xdata.png") xImage.FillMode = canvas.ImageFillContain content := container.NewGridWithRows(2, fImage, xImage) - w.Resize(fyne.NewSize(500, 600)) - w.SetContent(content) - w.ShowAndRun() - fmt.Println("This was executed after the graphics finished") +// w.Resize(fyne.NewSize(500, 600)) +// w.SetContent(content) + // w.ShowAndRun() + return content } diff --git a/ui/scrnplt.goat b/ui/scrnplt.goat deleted file mode 100644 index 470aa0e79ae7daaf2bbe61c82d582c23a3b9e727..0000000000000000000000000000000000000000 --- a/ui/scrnplt.goat +++ /dev/null @@ -1,32 +0,0 @@ -// 25 Jan 2020 -// Given a buffer or two with a plot picture, send it to the screen - -// I think this might be better in its own package -package ui - -import ( - "bytes" - "fmt" - "image/color" // need this for the green ? - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" -) - -func Scrnplt (b []byte) { - fmt.Println ("scrnplt says buf size", len(b), "first few bytes:", string(b[:10])) - a := app.New() - w := a.NewWindow("container") - image := canvas.NewImageFromReader(bytes.NewReader(b), "boo.svg") - green := color.NRGBA{R: 0, G: 180, B: 0, A: 255} - text1 := canvas.NewText("Hello", green); print(text1) - text2 := canvas.NewText("There", green) - text2.Move(fyne.NewPos(20, 20)) - content := container.NewWithoutLayout( image) // no more text 1, text2 - // content := container.New(layout.NewGridLayout(2), text1, text2) - - w.SetContent(content) - - w.ShowAndRun() -} diff --git a/ui/ui_run.go b/ui/ui_run.go new file mode 100644 index 0000000000000000000000000000000000000000..ef2ff0ad244a687d956520366f072b4e892b7db9 --- /dev/null +++ b/ui/ui_run.go @@ -0,0 +1,43 @@ +// 25 Jan 2020 +// Given a buffer or two with a plot picture, send it to the screen + +// +build !no_gfx + +package ui + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" + + "example.com/ackley_mc/mc_work" +) + + +func initIn () *widget.Card { + return widget.NewCard("input screen", "click on run to start a calc", nil) +} + +func UiDoRun (mcPrm *mcwork.McPrm) error { + + a := app.NewWithID("Monte Carlo") + w := a.NewWindow ("Monte Carlo") + quitbutton := widget.NewButton ("quit .. click somwhere in here", a.Quit) + chn := make(chan workstatus) + inputTabCallback := func() (*fyne.Container) { + a, _ := inputTab(mcPrm, w, chn) + return a + } + t1 := container.NewTabItem("input tab", inputTabCallback()) + cntrOut := fyne.NewContainer() + t2 := container.NewTabItem("output tab", cntrOut) + t3 := container.NewTabItem("quit me", quitbutton) + appTab := container.NewAppTabs(t1, t2, t3) + w.SetContent(appTab) + breaker() + go outputTab(chn, cntrOut) + w.ShowAndRun() + close (chn) + return nil +}