From f00519d5ba74f0b22eb0f0edea1bf0c3696e268d Mon Sep 17 00:00:00 2001
From: "Andrew E. Torda" <torda@zbh.uni-hamburg.de>
Date: Wed, 5 Jan 2022 22:01:53 +0100
Subject: [PATCH] If we expand the range, as we do, we have to fix up the range
 structure in GetTicks().

---
 ackwork/ackwork_test.go | 13 +++---
 ackwork/dorun.go        | 31 +++++++++++++-
 ackwork/plot.go         | 95 ++++++++++++++++++++++++++---------------
 ackwork/set_suffix.go   | 37 ++++++++++++++++
 doc.go                  |  1 +
 go.mod                  |  2 +
 go.sum                  |  2 -
 main.go                 |  2 +-
 8 files changed, 137 insertions(+), 46 deletions(-)
 create mode 100644 ackwork/set_suffix.go

diff --git a/ackwork/ackwork_test.go b/ackwork/ackwork_test.go
index 5e9a7af..9d4c3aa 100644
--- a/ackwork/ackwork_test.go
+++ b/ackwork/ackwork_test.go
@@ -118,13 +118,14 @@ func TestCsv(t *testing.T) {
 }
 
 var s1dplot = `
-ini_temp 1
-final_temp 0.05
-x_ini 15
-n_step 10000
+ini_temp 0.095
+final_temp 0.06
+x_ini 15,10,11,12,14,18,-12,-8,-9
+n_step 100000
 n_output 5000
-fOutName testanneal1d
-fPltName testplot.svg`
+fOutName test9d
+fPltName testplot.svg
+xPltName testrajplt`
 
 var plotTest = []string{
 	s1dplot,
diff --git a/ackwork/dorun.go b/ackwork/dorun.go
index 7dd1289..af7290d 100644
--- a/ackwork/dorun.go
+++ b/ackwork/dorun.go
@@ -43,7 +43,7 @@ type cprm struct { // parameters calculated from input
 	plotx    []float64      // Why float 64 ? Because the plotting libraries
 	plotf    []float64      // Function values for plotting
 	plotTmprtr []float64    // Temperature values for plotting
-	plotXtrj [][]float64    // for plotting trajectories
+	plotXtrj []float32    // for plotting trajectories
 }
 
 // setupRun does things like get the cooling rate, seed the random numbers.
@@ -95,6 +95,20 @@ func setupRun(mcPrm *mcPrm, cprm *cprm) error {
 			cprm.plotTmprtr = make([]float64, 0, n_alloc)
 		}
 	}
+	if mcPrm.xPltName != "" {
+		var err error
+		if mcPrm.xPltName, err = setSuffix(mcPrm.xPltName, ".png"); err != nil {
+			return fmt.Errorf("plot filename: %w", err)
+		}
+		if cprm.xPlt, err = os.Create(mcPrm.xPltName); err != nil {
+			return err
+		}
+		cprm.xplotme = true
+		n_alloc := mcPrm.nStep / 5
+		n_dim := len(mcPrm.xIni)
+		cprm.plotXtrj = make([]float32, 0, int(n_alloc) * n_dim)
+        // Should check that cprm.plotx is allocated for the step number
+	}
 	return nil
 }
 
@@ -107,7 +121,6 @@ func newx(xold []float32, xT []float32, rand *rand.Rand, xDlta float32) {
 		xT[i] = x + t
 	}
 }
-func breaker() {}
 
 // plotFval adds a point to the data to be plotted. At the start, we check
 // if we have to plot at all. This is done here, to make the main loop
@@ -150,6 +163,13 @@ func printfVal (fOut io.Writer, x []float32, n uint32, tmprtr float64, fOld floa
 	fmt.Fprintln(fOut)
 }
 
+// plotXtrj adds the next x coordinates to the pltXtrj for plotting at
+// the end
+func plotXtrj(n uint32, x []float32, pltXtrj *[]float32, xplotme bool) {
+	if ! xplotme { return }
+	*pltXtrj = append(*pltXtrj, x...)
+}
+
 // doRun does a Monte Carlo run. Although single precision is fine for the
 // coordinates and function, we use double precision for the temperature.
 func doRun(mcPrm *mcPrm) error {
@@ -177,7 +197,9 @@ func doRun(mcPrm *mcPrm) error {
 	nRunAcc := nRunAccAdj // Every nRunAccAdj, try adjusting the step size.
 	const runMult = 0.99
 	xDlta := mcPrm.xDlta // Adaptable step size
+	// if either plot is running, save the step.
 	plotFval(0, fOld, tmprtr, &cprm.plotx, &cprm.plotf, &cprm.plotTmprtr, cprm.fplotme)
+	plotXtrj(0, x, &cprm.plotXtrj, cprm.xplotme)
 	for n := uint32(0); n < mcPrm.nStep; n++ {
 		var acc bool
 		nout--
@@ -209,6 +231,7 @@ func doRun(mcPrm *mcPrm) error {
 			copy(x, xT)
 			fOld = fTrial
 			plotFval(n+1, fTrial, tmprtr, &cprm.plotx, &cprm.plotf, &cprm.plotTmprtr,  cprm.fplotme)
+			plotXtrj(n+1, x, &cprm.plotXtrj, cprm.xplotme)
 		} else { // update the running estimate of acceptance
 			runAcc = runMult * runAcc
 		}
@@ -217,6 +240,10 @@ func doRun(mcPrm *mcPrm) error {
 		}
 	}
 	err := plotfWrt(cprm.plotx, cprm.plotf, cprm.plotTmprtr, cprm.fPlt, cprm.fplotme)
+	if err != nil {
+		return err }
+	err = plotxWrt (&cprm, len(mcPrm.xIni))
+	
 	fmt.Println("n accepted:", nAcc, "of", mcPrm.nStep+1)
 	return err
 }
diff --git a/ackwork/plot.go b/ackwork/plot.go
index d08b68c..3d1478c 100644
--- a/ackwork/plot.go
+++ b/ackwork/plot.go
@@ -13,48 +13,16 @@
 package ackwork
 
 import (
-	"errors"
 	"fmt"
 	"io"
 	"math"
 	"os"
-	"path/filepath"
 
 	"github.com/wcharczuk/go-chart/v2"
 	"github.com/wcharczuk/go-chart/v2/drawing"
 	"gitlab.rrz.uni-hamburg.de/Bae5157/axticks"
 )
 
-// setSuffix takes a name and makes sure the desired suffix is at the end
-// of the filename.
-func setSuffix(fname, suffix string) (string, error) {
-	if len(fname) == 0 {
-		return "", errors.New("setSuffix given empty fname")
-	}
-	if suffix == "" { // no suffix might not be an error. Just
-		if fname[len(fname)-1] == '.' { // remove any trailing dot
-			fname = fname[0 : len(fname)-1]
-		}
-		if len(fname) > 0 {
-			return fname, nil
-		}
-		return "", errors.New("setSuffix got empty filename")
-	}
-
-	if suffix[0] != '.' {
-		suffix = "." + suffix
-	}
-	oldExt := filepath.Ext(fname)
-	switch oldExt {
-	case suffix:
-		return fname, nil
-	case "":
-		return fname + suffix, nil
-	default:
-		return fname[0:len(fname)-len(oldExt)] + suffix, nil
-	}
-}
-
 // maketicks gives reasonable default tick locations
 func maketicks(axisDscrpt axticks.AxisDscrpt, prcsn int) []chart.Tick {
 	xmin, delta, prcsn := axisDscrpt.Xmin, axisDscrpt.Delta, axisDscrpt.Prcsn
@@ -85,6 +53,8 @@ func (rng *range2) GetTicks(r chart.Renderer, cs chart.Style, vf chart.ValueForm
 		fmt.Fprintln(os.Stderr, "GetTicks error:", err)
 		return nil
 	} else {
+		rng.Min = a.Xmin
+		rng.Max = a.Xmax
 		return maketicks(a, a.Prcsn)
 	}
 }
@@ -112,7 +82,7 @@ func plotfWrt(xdata, ydata []float64, tmprtrData []float64,
 
 	tmprtrAxis := chart.YAxis{
 		Name:      "temperature",
-		NameStyle: chart.Style{TextRotationDegrees: math.SmallestNonzeroFloat64},
+		NameStyle: chart.Style{TextRotationDegrees: 360},
 		Range:     &range2{},
 	}
 
@@ -141,8 +111,8 @@ func plotfWrt(xdata, ydata []float64, tmprtrData []float64,
 
 		Background: chart.Style{
 			Padding: chart.Box{
-				Left:  75,
-				Right: 25,
+				Left:  50,
+				Right: 75,
 			},
 			FillColor: drawing.ColorTransparent,
 		},
@@ -153,3 +123,58 @@ func plotfWrt(xdata, ydata []float64, tmprtrData []float64,
 
 	return nil
 }
+func breaker() {}
+
+// plotxWrt
+// For each dimension, allocate space. Copy elements over from the long array.
+// Then call the plotter on each series in turn. I think I have to make a
+// slice of continuous series and then add them into the chart structure.
+func plotxWrt(cprm *cprm, ndim int) error {
+	if !cprm.xplotme {
+		return nil
+	}
+
+	type f64 []float64
+	len_used := len(cprm.plotx)
+	xdata := make([]f64, ndim)
+	fmt.Println("plotxWrt has", ndim, "dimensions and len_used is", len_used)
+	for i := 0; i < ndim; i++ {
+		xdata[i] = make([]float64, len_used)
+	}
+	breaker()
+	var n int
+	for i := 0; i < len_used; i++ {
+		for j := 0; j < ndim; j++ {
+			xdata[j][i] = float64(cprm.plotXtrj[n])
+			n++
+		}
+	}
+	series := make([]chart.Series, ndim)
+	for i := 0; i < ndim; i++ {
+		series[i] = chart.ContinuousSeries{
+			Name:    fmt.Sprintf("dimension %d", i),
+			XValues: cprm.plotx,
+			YValues: xdata[i],
+		}
+	}
+	graph := chart.Chart{
+		Title: fmt.Sprintf("X trajectories %d dimension", ndim),
+		XAxis: chart.XAxis{Name: "step", Range: &range2{}},
+		YAxis: chart.YAxis{
+			Name:      "x coord",
+			Range:     &range2{},
+			AxisType:  chart.YAxisSecondary,
+			NameStyle: chart.Style{TextRotationDegrees: 360},
+		},
+		Series: series,
+		Background: chart.Style{
+			Padding: chart.Box{
+				Left: 75,
+			},
+		},
+	}
+	if err := graph.Render(chart.PNG, cprm.xPlt); err != nil {
+		return fmt.Errorf("plotting X trajectories: %w", err)
+	}
+	return nil
+}
diff --git a/ackwork/set_suffix.go b/ackwork/set_suffix.go
new file mode 100644
index 0000000..d10f001
--- /dev/null
+++ b/ackwork/set_suffix.go
@@ -0,0 +1,37 @@
+// a utility
+package ackwork
+
+import (
+	"errors"
+		"path/filepath"
+)
+
+// setSuffix takes a name and makes sure the desired suffix is at the end
+// of the filename.
+func setSuffix(fname, suffix string) (string, error) {
+	if len(fname) == 0 {
+		return "", errors.New("setSuffix given empty fname")
+	}
+	if suffix == "" { // no suffix might not be an error. Just
+		if fname[len(fname)-1] == '.' { // remove any trailing dot
+			fname = fname[0 : len(fname)-1]
+		}
+		if len(fname) > 0 {
+			return fname, nil
+		}
+		return "", errors.New("setSuffix got empty filename")
+	}
+
+	if suffix[0] != '.' {
+		suffix = "." + suffix
+	}
+	oldExt := filepath.Ext(fname)
+	switch oldExt {
+	case suffix:
+		return fname, nil
+	case "":
+		return fname + suffix, nil
+	default:
+		return fname[0:len(fname)-len(oldExt)] + suffix, nil
+	}
+}
diff --git a/doc.go b/doc.go
index fe74335..8f71b55 100644
--- a/doc.go
+++ b/doc.go
@@ -6,6 +6,7 @@
 //   Ackley input_file
 // 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
 /* 
 	{"ini_temp", "20"},
 	{"final_temp", "1"},
diff --git a/go.mod b/go.mod
index b3e69c3..d9e56d6 100644
--- a/go.mod
+++ b/go.mod
@@ -6,3 +6,5 @@ require (
 	github.com/wcharczuk/go-chart/v2 v2.1.0
 	gitlab.rrz.uni-hamburg.de/Bae5157/axticks v0.0.0-20211227145643-dc5ef95d7dad
 )
+
+replace gitlab.rrz.uni-hamburg.de/Bae5157/axticks => ../axticks
diff --git a/go.sum b/go.sum
index 520af54..7213416 100644
--- a/go.sum
+++ b/go.sum
@@ -2,8 +2,6 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 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=
-gitlab.rrz.uni-hamburg.de/Bae5157/axticks v0.0.0-20211227145643-dc5ef95d7dad h1:IBCLghidSwZff0/3RuvTLDnHgK9SaZHZ/cXWSjimEt0=
-gitlab.rrz.uni-hamburg.de/Bae5157/axticks v0.0.0-20211227145643-dc5ef95d7dad/go.mod h1:5CNHyqeidRypmIVTnQksGqnmP56Oshw1Zv1nlUezrpQ=
 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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/main.go b/main.go
index 60637ae..0eb00a0 100644
--- a/main.go
+++ b/main.go
@@ -21,7 +21,7 @@ const (
 
 func main() {
 	if err := ackwork.MyMain (); err != nil {
-		fmt.Println (os.Stderr, err)
+		fmt.Fprintln (os.Stderr, err)
 		os.Exit (exitFailure)
 	}
 	os.Exit (exitSuccess)
-- 
GitLab