├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── arrayfuncs.go.tpl ├── build.cmd ├── genarray.go ├── gonum.go.tpl ├── gonum_test.go.tpl ├── na32 ├── arrayfuncs.go ├── arrayfuncs_amd64.go ├── arrayfuncs_amd64.s ├── gonum.go ├── gonum_test.go ├── math_gen.go ├── narray.go └── narray_test.go ├── na64 ├── arrayfuncs.go ├── arrayfuncs_amd64.go ├── arrayfuncs_amd64.s ├── gonum.go ├── gonum_test.go ├── math_gen.go ├── narray.go └── narray_test.go ├── narray.go.tpl ├── narray_test.go.tpl └── testfmt.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # make sure we don't push the executable. 2 | gjoa 3 | !gjoa/ 4 | 5 | *.o 6 | *.a 7 | *.so 8 | *.out 9 | *.test 10 | out.* 11 | 12 | # Folders 13 | _obj 14 | _test 15 | tmp 16 | old 17 | log* 18 | out 19 | 20 | *.exe 21 | *.bak 22 | *~ 23 | *.sublime-workspace 24 | 25 | darwin 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3 5 | - 1.4 6 | - tip 7 | 8 | script: 9 | - go test -cpu=1,2,4 ./na64 10 | - go test -cpu=4 -race ./na64 11 | - go test -cpu=1,2,4 ./na32 12 | - go test -cpu=4 -race ./na32 13 | - go run genarray.go -compare 14 | - chmod ugo+x testfmt.sh 15 | - ./testfmt.sh 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 AKUALAB INC. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of AKUALAB INC. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Package narray [![Build Status](https://travis-ci.org/akualab/narray.svg)](https://travis-ci.org/akualab/narray) 2 | 3 | Package narray provides functions to opearate on multidimensional floating-point arrays. 4 | 5 | Packages for types float32 and float64 are automatically generated. These options makes it 6 | possible to find the trade offs between precision and computation speed. 7 | 8 | The elementwise operations are also generated automatically by scraping the standard math package. 9 | 10 | Various functions are optimized using assembly code for amd64 acrhitecture. 11 | 12 | To easily swap the narray package in your project, import using an alias as follows: 13 | 14 | ``` 15 | import ( 16 | narray "github.com/akualab/narray/na64" 17 | ) 18 | ``` 19 | 20 | ## Download 21 | 22 | ### Type float64 package: 23 | go get -u github.com/akualab/narray/na64 24 | 25 | ### Type float32 package: 26 | go get -u github.com/akualab/narray/na32 27 | 28 | ## Documentation 29 | * [Godoc na64](http://godoc.org/github.com/akualab/narray/na64) 30 | * [Godoc na32](http://godoc.org/github.com/akualab/narray/na32) 31 | 32 | ## Code Generation 33 | Code generation is only done by the narray package developers. End users don't have to generate any code. 34 | ``` 35 | go run genarray.go 36 | ``` 37 | 38 | ## Credits 39 | 40 | All the assembly optimization work and more was done by https://github.com/klauspost . THANKS!! 41 | -------------------------------------------------------------------------------- /arrayfuncs.go.tpl: -------------------------------------------------------------------------------- 1 | // +build !amd64 2 | 3 | package {{.Package}} 4 | 5 | import ( 6 | "math" 7 | ) 8 | 9 | // These are the fallbacks that are used when not on AMD64 platform. 10 | 11 | // divSlice divides two slices 12 | // Assumptions the assembly can make: 13 | // out != nil, a != nil, b != nil 14 | // len(out) == len(a) == len(b) 15 | func divSlice(out, a, b []{{.Format}}) { 16 | for i := 0; i < len(out); i++ { 17 | out[i] = a[i] / b[i] 18 | } 19 | } 20 | 21 | // addSlice adds two slices 22 | // Assumptions the assembly can make: 23 | // out != nil, a != nil, b != nil 24 | // len(out) == len(a) == len(b) 25 | func addSlice(out, a, b []{{.Format}}) { 26 | for i := 0; i < len(out); i++ { 27 | out[i] = a[i] + b[i] 28 | } 29 | } 30 | 31 | // subSlice subtracts two slices 32 | // Assumptions the assembly can make: 33 | // out != nil, a != nil, b != nil 34 | // len(out) == len(a) == len(b) 35 | func subSlice(out, a, b []{{.Format}}) { 36 | for i := 0; i < len(out); i++ { 37 | out[i] = a[i] - b[i] 38 | } 39 | } 40 | 41 | // mulSlice multiply two slices 42 | // Assumptions the assembly can make: 43 | // out != nil, a != nil, b != nil 44 | // len(out) == len(a) == len(b) 45 | func mulSlice(out, a, b []{{.Format}}) { 46 | for i := 0; i < len(out); i++ { 47 | out[i] = a[i] * b[i] 48 | } 49 | } 50 | 51 | // minSlice returns lowest valus of two slices 52 | // Assumptions the assembly can make: 53 | // out != nil, a != nil, b != nil 54 | // len(out) == len(a) == len(b) 55 | func minSlice(out, a, b []{{.Format}}) { 56 | for i := 0; i < len(out); i++ { 57 | if a[i] < b[i] { 58 | out[i] = a[i] 59 | } else { 60 | out[i] = b[i] 61 | } 62 | } 63 | } 64 | 65 | // maxSlice return maximum of two slices 66 | // Assumptions the assembly can make: 67 | // out != nil, a != nil, b != nil 68 | // len(out) == len(a) == len(b) 69 | func maxSlice(out, a, b []{{.Format}}) { 70 | for i := 0; i < len(out); i++ { 71 | if a[i] > b[i] { 72 | out[i] = a[i] 73 | } else { 74 | out[i] = b[i] 75 | } 76 | } 77 | } 78 | 79 | // csignSlice returns a value with the magnitude of a and the sign of b 80 | // for each element in the slice. 81 | // Assumptions the assembly can make: 82 | // out != nil, a != nil, b != nil 83 | // len(out) == len(a) == len(b) 84 | func csignSlice(out, a, b []{{.Format}}) { 85 | {{if .Float64}} const sign = 1 << 63 {{end}}{{if .Float32}} const sign = 1 << 31 {{end}} 86 | for i := 0; i < len(out); i++ { 87 | {{if .Float64}} out[i] = math.Float64frombits(math.Float64bits(a[i])&^sign | math.Float64bits(b[i])&sign){{end}} 88 | {{if .Float32}} out[i] = math.Float32frombits(math.Float32bits(a[i])&^sign | math.Float32bits(b[i])&sign){{end}} 89 | } 90 | } 91 | 92 | // cdivSlice will return c / values of the array 93 | // Assumptions the assembly can make: 94 | // out != nil, a != nil 95 | // len(out) == len(a) 96 | func cdivSlice(out, a []{{.Format}}, c {{.Format}}) { 97 | for i := 0; i < len(out); i++ { 98 | out[i] = c / a[i] 99 | } 100 | } 101 | 102 | // cmulSlice will return c * values of the array 103 | // Assumptions the assembly can make: 104 | // out != nil, a != nil 105 | // len(out) == len(a) 106 | func cmulSlice(out, a []{{.Format}}, c {{.Format}}) { 107 | for i := 0; i < len(out); i++ { 108 | out[i] = c * a[i] 109 | } 110 | } 111 | 112 | // caddSlice will return c * values of the array 113 | // Assumptions the assembly can make: 114 | // out != nil, a != nil 115 | // len(out) == len(a) 116 | func caddSlice(out, a []{{.Format}}, c {{.Format}}) { 117 | for i := 0; i < len(out); i++ { 118 | out[i] = c + a[i] 119 | } 120 | } 121 | 122 | // addScaledSlice adds a scaled narray elementwise. 123 | // y = y + a * x 124 | // Assumptions the assembly can make: 125 | // y != nil, a != nil 126 | // len(x) == len(y) 127 | func addScaledSlice(y, x []{{.Format}}, a {{.Format}}) { 128 | for i, v := range x { 129 | y[i] += v * a 130 | } 131 | } 132 | 133 | // sqrtSlice will return math.Sqrt(values) of the array 134 | // Assumptions the assembly can make: 135 | // out != nil, a != nil 136 | // len(out) == len(a) 137 | func sqrtSlice(out, a []{{.Format}}) { 138 | for i := 0; i < len(out); i++ { 139 | out[i] = {{.Format}}(math.Sqrt(float64(a[i]))) 140 | } 141 | } 142 | 143 | // minSliceElement will the smallest value of the slice 144 | // Assumptions the assembly can make: 145 | // a != nil 146 | // len(a) > 0 147 | func minSliceElement(a []{{.Format}}) {{.Format}} { 148 | min := a[0] 149 | for i := 1; i < len(a); i++ { 150 | if a[i] < min { 151 | min = a[i] 152 | } 153 | } 154 | return min 155 | } 156 | 157 | // maxSliceElement will the biggest value of the slice 158 | // Assumptions the assembly can make: 159 | // a != nil 160 | // len(a) > 0 161 | func maxSliceElement(a []{{.Format}}) {{.Format}} { 162 | max := a[0] 163 | for i := 1; i < len(a); i++ { 164 | if a[i] > max { 165 | max = a[i] 166 | } 167 | } 168 | return max 169 | } 170 | 171 | // sliceSum will return the sum of all elements of the slice 172 | // Assumptions the assembly can make: 173 | // a != nil 174 | // len(a) >= 0 175 | func sliceSum(a []{{.Format}}) {{.Format}} { 176 | sum := {{.Format}}(0.0) 177 | for _, v := range a { 178 | sum += v 179 | } 180 | return sum 181 | } 182 | 183 | // absSlice will return math.Abs(values) of the array 184 | // Assumptions the assembly can make: 185 | // out != nil, a != nil 186 | // len(out) == len(a) 187 | func absSlice(out, a []{{.Format}}) { 188 | for i, v := range a { 189 | out[i] = {{.Format}}(math.Abs(float64(v))) 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | go run genarray.go && go test ./... 2 | -------------------------------------------------------------------------------- /genarray.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:generate go run genarray.go 7 | 8 | package main 9 | 10 | import ( 11 | "bufio" 12 | "bytes" 13 | "flag" 14 | "fmt" 15 | "go/format" 16 | "io/ioutil" 17 | "log" 18 | "os" 19 | "path/filepath" 20 | "regexp" 21 | "runtime" 22 | "strings" 23 | "text/template" 24 | ) 25 | 26 | var exclude = map[string]bool{"Sqrt": true, "Abs": true, "Min": true, "Max": true, "Copysign": true} 27 | 28 | // Match lines with pattern: "func Log(x float64) float64" 29 | var re1 = regexp.MustCompile("^func ([A-Z][[:alnum:]]*)[(][[:alnum:]]+ float64[)] float64") 30 | 31 | // Match lines with pattern: "func Remainder(x, y float64) float64" 32 | var re2 = regexp.MustCompile("^func ([A-Z][[:alnum:]]*)[(][[:alnum:]]+, [[:alnum:]]+ float64[)] float64") 33 | 34 | var compare = flag.Bool("compare", false, "Compare generated output to ondisk output. Returnvalue is 0 on match") 35 | 36 | type genType struct { 37 | Format string 38 | Package string 39 | Float64 bool 40 | Float32 bool 41 | Biggest string 42 | Smallest string 43 | } 44 | 45 | var generateTypes = []genType{ 46 | genType{Format: "float32", Package: "na32", Float32: true, Biggest: "float32(math.MaxFloat32)", Smallest: "float32(-math.MaxFloat32)"}, 47 | genType{Format: "float64", Package: "na64", Float64: true, Biggest: "math.MaxFloat64", Smallest: "-math.MaxFloat64"}, 48 | } 49 | 50 | func main() { 51 | flag.Parse() 52 | ver := runtime.Version() 53 | if strings.Contains(ver, "go1.3") || strings.Contains(ver, "go1.2") { 54 | log.Println("Generation requires go v1.4 or later, this is", ver) 55 | // We exit with 0, so we don't fail tests. 56 | os.Exit(0) 57 | } 58 | for _, t := range generateTypes { 59 | fmt.Printf("Generating code for format %s in package %s\n", t.Format, t.Package) 60 | genMath(t) 61 | genFiles(t) 62 | } 63 | } 64 | 65 | // Generates math package bindings. 66 | func genMath(t genType) { 67 | var g Generator 68 | n1, n2 := names() 69 | outputName := t.Package + string(os.PathSeparator) + "math_gen.go" 70 | fmt.Printf("Generating %d narray functions:\n1 param:%s\n2 params:%s\n", len(n1)+len(n2), n1, n2) 71 | 72 | g.Printf("// generated by narray; DO NOT EDIT\n") 73 | g.Printf("// more info at github.com/akualab/narray\n") 74 | g.Printf("\n") 75 | g.Printf("package %s\n\n", t.Package) 76 | g.Printf("import \"math\"\n\n") 77 | 78 | for _, name := range n1 { 79 | 80 | g.Printf("// %s applies math.%s() elementwise to a multidimensional array.\n", name, name) 81 | g.Printf("// See math package in standard lib for details.\n//\n") 82 | g.Printf("// If 'out' is nil a new array is created.\n") 83 | g.Printf("// Will panic if 'out' and 'in' shapes don't match.\n") 84 | g.Printf("func %s(out, in *NArray) *NArray {\n", name) 85 | g.Printf(" if out == nil {\n") 86 | g.Printf(" out = New(in.Shape...)\n") 87 | g.Printf(" } else if !EqualShape(out, in) {\n") 88 | g.Printf(" panic(\"%s:narrays must have equal shape.\")\n", name) 89 | g.Printf(" }\n") 90 | g.Printf(" for k,v := range in.Data {\n") 91 | g.Printf(" out.Data[k] = %s(math.%s(float64(v)))\n", t.Format, name) 92 | g.Printf(" }\n") 93 | g.Printf(" return out\n") 94 | g.Printf("}\n") 95 | g.Printf("\n") 96 | } 97 | 98 | for _, name := range n2 { 99 | 100 | g.Printf("// %s applies math.%s() elementwise to two multidimensional arrays.\n", name, name) 101 | g.Printf("// See math package in standard lib for details.\n//\n") 102 | g.Printf("// If out is nil a new array is created.\n") 103 | g.Printf("// Will panic if 'out', 'a' and 'b' shapes don't match.\n") 104 | g.Printf("func %s(out, a, b *NArray) *NArray {\n", name) 105 | g.Printf(" if out == nil {\n") 106 | g.Printf(" out = New(a.Shape...)\n") 107 | g.Printf(" }\n") 108 | g.Printf(" if !EqualShape(out, a, b) {\n") 109 | g.Printf(" panic(\"%s:narrays must have equal shape.\")\n", name) 110 | g.Printf(" }\n") 111 | g.Printf(" for k,v := range a.Data {\n") 112 | g.Printf(" out.Data[k] = %s(math.%s(float64(v), float64(b.Data[k])))\n", t.Format, name) 113 | g.Printf(" }\n") 114 | g.Printf(" return out\n") 115 | g.Printf("}\n") 116 | g.Printf("\n") 117 | } 118 | 119 | // Format the output. 120 | src := g.format() 121 | 122 | // Write to file. 123 | if *compare { 124 | existing, err := ioutil.ReadFile(outputName) 125 | if err != nil { 126 | log.Fatalf("opening existing: %s", err) 127 | } 128 | if bytes.Compare(src, existing) != 0 { 129 | log.Println("Mismatch in file", outputName) 130 | os.Exit(1) 131 | } 132 | } else { 133 | err := ioutil.WriteFile(outputName, src, 0644) 134 | if err != nil { 135 | log.Fatalf("writing output: %s", err) 136 | } 137 | } 138 | } 139 | 140 | // Get list of function names with 1 or 2 parameters 141 | func names() ([]string, []string) { 142 | 143 | var names []string 144 | var names2 []string 145 | for _, path := range goFiles() { 146 | 147 | f, err := os.Open(path) 148 | if err != nil { 149 | panic(err) 150 | } 151 | scanner := bufio.NewScanner(f) 152 | for scanner.Scan() { 153 | line := scanner.Text() 154 | name, ok := f2f(line, re1) 155 | _, ok2 := exclude[name] 156 | if ok && !ok2 { 157 | names = append(names, name) 158 | } 159 | name, ok = f2f(line, re2) 160 | _, ok2 = exclude[name] 161 | if ok && !ok2 { 162 | names2 = append(names2, name) 163 | } 164 | } 165 | if err := scanner.Err(); err != nil { 166 | fmt.Fprintf(os.Stderr, "error reading file [%s]: %s", path, err) 167 | } 168 | err = f.Close() 169 | if err != nil { 170 | panic(err) 171 | } 172 | } 173 | return names, names2 174 | } 175 | 176 | // Get list of Go files to analyze. 177 | func goFiles() []string { 178 | 179 | mathDir := filepath.Join(runtime.GOROOT(), "src", "math") 180 | var files []string 181 | err := filepath.Walk(mathDir, func(path string, info os.FileInfo, err error) error { 182 | 183 | if err != nil { 184 | return err 185 | } 186 | if info.IsDir() { 187 | return nil 188 | } 189 | 190 | if strings.HasSuffix(path, "_test.go") { 191 | return nil 192 | } 193 | 194 | if strings.HasSuffix(path, ".go") { 195 | files = append(files, path) 196 | } 197 | return nil 198 | }) 199 | 200 | if err != nil { 201 | panic(err) 202 | } 203 | return files 204 | } 205 | 206 | // Returns a function name if it has the re pattern. 207 | func f2f(line string, re *regexp.Regexp) (string, bool) { 208 | 209 | result := re.FindStringSubmatch(line) 210 | if len(result) == 2 { 211 | return result[1], true 212 | } 213 | return "", false 214 | } 215 | 216 | // Generator holds the state of the analysis. Primarily used to buffer 217 | // the output for format.Source. 218 | type Generator struct { 219 | buf bytes.Buffer // Accumulated output. 220 | } 221 | 222 | func (g *Generator) Printf(format string, args ...interface{}) { 223 | fmt.Fprintf(&g.buf, format, args...) 224 | } 225 | 226 | func (g *Generator) Write(p []byte) (n int, err error) { 227 | return g.buf.Write(p) 228 | } 229 | 230 | // format returns the gofmt-ed contents of the Generator's buffer. 231 | func (g *Generator) format() []byte { 232 | src, err := format.Source(g.buf.Bytes()) 233 | if err != nil { 234 | // Should never happen, but can arise when developing this code. 235 | // The user can compile the output to see the error. 236 | log.Printf("warning: internal error: invalid Go generated: %s", err) 237 | log.Printf("warning: compile the package to analyze the error") 238 | return g.buf.Bytes() 239 | } 240 | return src 241 | } 242 | 243 | // Generate files from Templates 244 | // The arrays must match in order 245 | var outFiles = []string{"gonum.go", "gonum_test.go", "narray.go", "narray_test.go", "arrayfuncs.go"} 246 | var templateFiles = []string{"gonum.go.tpl", "gonum_test.go.tpl", "narray.go.tpl", "narray_test.go.tpl", "arrayfuncs.go.tpl"} 247 | 248 | // Generate files from templates 249 | func genFiles(t genType) { 250 | templ, err := template.ParseFiles(templateFiles...) 251 | if err != nil { 252 | panic(err) 253 | } 254 | for i, file := range outFiles { 255 | fmt.Printf("Generating file %s for type %s\n", file, t.Format) 256 | 257 | var g Generator 258 | g.Printf("// generated by narray; DO NOT EDIT\n\n") 259 | err = templ.ExecuteTemplate(&g, templateFiles[i], t) 260 | if err != nil { 261 | panic(err) 262 | } 263 | // Format the output. 264 | src := g.format() 265 | 266 | outputName := t.Package + string(os.PathSeparator) + file 267 | if *compare { 268 | existing, err := ioutil.ReadFile(outputName) 269 | if err != nil { 270 | log.Fatalf("opening existing: %s", err) 271 | } 272 | if bytes.Compare(src, existing) != 0 { 273 | log.Println("Mismatch in file", outputName) 274 | os.Exit(1) 275 | } 276 | } else { 277 | // Write to file. 278 | err = ioutil.WriteFile(outputName, src, 0644) 279 | if err != nil { 280 | log.Fatalf("writing output: %s", err) 281 | } 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /gonum.go.tpl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package {{.Package}} 7 | 8 | // Matrix is as an NArray of rank 2 that satisfies the gonum Matrix interfaces. 9 | type Matrix NArray 10 | 11 | // Vector is as an NArray of rank 1 that satisfies the gonum Vectorer interface. 12 | type Vector NArray 13 | 14 | // Matrix creates a subarray of rank 2. 15 | // Equivalent to SubArray but restricted to the case where the 16 | // resulting subarray has rank=2. (It will panic otherwise.) 17 | // See SubArray for details. 18 | func (na *NArray) Matrix(query ...int) *Matrix { 19 | 20 | mat := na.SubArray(query...) 21 | if len(mat.Shape) != 2 { 22 | panic("matrix must have rank equal two") 23 | } 24 | return (*Matrix)(mat) 25 | } 26 | 27 | // Dims returns the dimensions of a Matrix. 28 | func (mat *Matrix) Dims() (r, c int) { 29 | na := (*NArray)(mat) 30 | return na.Shape[0], na.Shape[1] 31 | } 32 | 33 | // At returns the value of a matrix element at (r, c). It will panic if r or c are 34 | // out of bounds for the matrix. 35 | func (mat *Matrix) At(r, c int) {{.Format}} { 36 | na := (*NArray)(mat) 37 | return na.At(r, c) 38 | } 39 | 40 | // Set alters the matrix element at (r, c) to v. It will panic if r or c are out of 41 | // bounds for the matrix. 42 | func (mat *Matrix) Set(r, c int, v {{.Format}}) { 43 | na := (*NArray)(mat) 44 | na.Set(v, r, c) 45 | } 46 | 47 | // String returns vector as a printable string. 48 | func (mat *Matrix) String() string { 49 | na := (*NArray)(mat) 50 | return na.String() 51 | } 52 | 53 | // Vector creates a subarray of rank 1. 54 | // Equivalent to SubArray but restricted to the case where the 55 | // resulting subarray has rank=1. (It will panic otherwise.) 56 | // See SubArray for details. 57 | // 58 | // Example, given a 5x10 matrix (rank=2), return the vector 59 | // of dim 10 for row idx=3: 60 | // 61 | // x := New(5,10) 62 | // y := x.Vector(3,-1) 63 | // // y = {x_30, x_31, ... , x_39} 64 | func (na *NArray) Vector(query ...int) *Vector { 65 | 66 | vec := na.SubArray(query...) 67 | if len(vec.Shape) != 1 { 68 | panic("vector must have rank equal one") 69 | } 70 | return (*Vector)(vec) 71 | } 72 | 73 | // Row returns a slice of {{.Format}} for the row specified. It will panic if the index 74 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 75 | // returned, if it is not nil the number of elements copied will be the minimum of the 76 | // length of the slice and the number of columns in the matrix. 77 | func (mat *Matrix) Row(dst []{{.Format}}, i int) []{{.Format}} { 78 | _, ncols := mat.Dims() 79 | if dst == nil { 80 | dst = make([]{{.Format}}, ncols, ncols) 81 | } 82 | for j, _ := range dst { 83 | dst[j] = mat.At(i, j) 84 | } 85 | return dst 86 | } 87 | 88 | // Col returns a slice of {{.Format}} for the column specified. It will panic if the index 89 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 90 | // returned, if it is not nil the number of elements copied will be the minimum of the 91 | // length of the slice and the number of rows in the matrix. 92 | func (mat *Matrix) Col(dst []{{.Format}}, j int) []{{.Format}} { 93 | nrows, _ := mat.Dims() 94 | if dst == nil { 95 | dst = make([]{{.Format}}, nrows, nrows) 96 | } 97 | for i, _ := range dst { 98 | dst[i] = mat.At(i, j) 99 | } 100 | return dst 101 | } 102 | 103 | // SetRow sets the values of the specified row to the values held in a slice of {{.Format}}. 104 | // It will panic if the index is out of bounds. The number of elements copied is 105 | // returned and will be the minimum of the length of the slice and the number of columns 106 | // in the matrix. 107 | func (mat *Matrix) SetRow(i int, src []{{.Format}}) int { 108 | 109 | numCopied := len(src) 110 | if len(src) > mat.Shape[1] { 111 | numCopied = mat.Shape[1] 112 | } 113 | for j := 0; j < numCopied; j++ { 114 | mat.Set(i, j, src[j]) 115 | } 116 | return numCopied 117 | } 118 | 119 | // SetCol sets the values of the specified column to the values held in a slice of {{.Format}}. 120 | // It will panic if the index is out of bounds. The number of elements copied is 121 | // returned and will be the minimum of the length of the slice and the number of rows 122 | // in the matrix. 123 | func (mat *Matrix) SetCol(j int, src []{{.Format}}) int { 124 | 125 | numCopied := len(src) 126 | if len(src) > mat.Shape[0] { 127 | numCopied = mat.Shape[0] 128 | } 129 | for i := 0; i < numCopied; i++ { 130 | mat.Set(i, j, src[i]) 131 | } 132 | return numCopied 133 | } 134 | 135 | // TODO finish implementing gonum interfaces. 136 | -------------------------------------------------------------------------------- /gonum_test.go.tpl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package {{.Package}} 7 | 8 | import "testing" 9 | 10 | func TestMatrix(t *testing.T) { 11 | 12 | mat := na234.Matrix(0, -1, -1) 13 | t.Log(mat) 14 | 15 | row0 := mat.Row(nil, 0) 16 | row1 := mat.Row(nil, 1) 17 | row2 := mat.Row(nil, 2) 18 | col0 := mat.Col(nil, 0) 19 | col1 := mat.Col(nil, 1) 20 | col2 := mat.Col(nil, 2) 21 | col3 := mat.Col(nil, 3) 22 | 23 | t.Log(row0) 24 | t.Log(row1) 25 | t.Log(row2) 26 | t.Log(col0) 27 | t.Log(col1) 28 | t.Log(col2) 29 | t.Log(col3) 30 | 31 | na := na234.SubArray(1, 2, -1) 32 | vec := na234.Vector(1, 2, -1) 33 | if !EqualValues(na, (*NArray)(vec), 0) { 34 | t.Fatalf("expected same values") 35 | } 36 | 37 | na = na234.SubArray(-1, 2, -1) 38 | mat = na234.Matrix(-1, 2, -1) 39 | if !EqualValues(na, (*NArray)(mat), 0) { 40 | t.Fatalf("expected same values") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /na32/arrayfuncs.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // +build !amd64 4 | 5 | package na32 6 | 7 | import ( 8 | "math" 9 | ) 10 | 11 | // These are the fallbacks that are used when not on AMD64 platform. 12 | 13 | // divSlice divides two slices 14 | // Assumptions the assembly can make: 15 | // out != nil, a != nil, b != nil 16 | // len(out) == len(a) == len(b) 17 | func divSlice(out, a, b []float32) { 18 | for i := 0; i < len(out); i++ { 19 | out[i] = a[i] / b[i] 20 | } 21 | } 22 | 23 | // addSlice adds two slices 24 | // Assumptions the assembly can make: 25 | // out != nil, a != nil, b != nil 26 | // len(out) == len(a) == len(b) 27 | func addSlice(out, a, b []float32) { 28 | for i := 0; i < len(out); i++ { 29 | out[i] = a[i] + b[i] 30 | } 31 | } 32 | 33 | // subSlice subtracts two slices 34 | // Assumptions the assembly can make: 35 | // out != nil, a != nil, b != nil 36 | // len(out) == len(a) == len(b) 37 | func subSlice(out, a, b []float32) { 38 | for i := 0; i < len(out); i++ { 39 | out[i] = a[i] - b[i] 40 | } 41 | } 42 | 43 | // mulSlice multiply two slices 44 | // Assumptions the assembly can make: 45 | // out != nil, a != nil, b != nil 46 | // len(out) == len(a) == len(b) 47 | func mulSlice(out, a, b []float32) { 48 | for i := 0; i < len(out); i++ { 49 | out[i] = a[i] * b[i] 50 | } 51 | } 52 | 53 | // minSlice returns lowest valus of two slices 54 | // Assumptions the assembly can make: 55 | // out != nil, a != nil, b != nil 56 | // len(out) == len(a) == len(b) 57 | func minSlice(out, a, b []float32) { 58 | for i := 0; i < len(out); i++ { 59 | if a[i] < b[i] { 60 | out[i] = a[i] 61 | } else { 62 | out[i] = b[i] 63 | } 64 | } 65 | } 66 | 67 | // maxSlice return maximum of two slices 68 | // Assumptions the assembly can make: 69 | // out != nil, a != nil, b != nil 70 | // len(out) == len(a) == len(b) 71 | func maxSlice(out, a, b []float32) { 72 | for i := 0; i < len(out); i++ { 73 | if a[i] > b[i] { 74 | out[i] = a[i] 75 | } else { 76 | out[i] = b[i] 77 | } 78 | } 79 | } 80 | 81 | // csignSlice returns a value with the magnitude of a and the sign of b 82 | // for each element in the slice. 83 | // Assumptions the assembly can make: 84 | // out != nil, a != nil, b != nil 85 | // len(out) == len(a) == len(b) 86 | func csignSlice(out, a, b []float32) { 87 | const sign = 1 << 31 88 | for i := 0; i < len(out); i++ { 89 | 90 | out[i] = math.Float32frombits(math.Float32bits(a[i])&^sign | math.Float32bits(b[i])&sign) 91 | } 92 | } 93 | 94 | // cdivSlice will return c / values of the array 95 | // Assumptions the assembly can make: 96 | // out != nil, a != nil 97 | // len(out) == len(a) 98 | func cdivSlice(out, a []float32, c float32) { 99 | for i := 0; i < len(out); i++ { 100 | out[i] = c / a[i] 101 | } 102 | } 103 | 104 | // cmulSlice will return c * values of the array 105 | // Assumptions the assembly can make: 106 | // out != nil, a != nil 107 | // len(out) == len(a) 108 | func cmulSlice(out, a []float32, c float32) { 109 | for i := 0; i < len(out); i++ { 110 | out[i] = c * a[i] 111 | } 112 | } 113 | 114 | // caddSlice will return c * values of the array 115 | // Assumptions the assembly can make: 116 | // out != nil, a != nil 117 | // len(out) == len(a) 118 | func caddSlice(out, a []float32, c float32) { 119 | for i := 0; i < len(out); i++ { 120 | out[i] = c + a[i] 121 | } 122 | } 123 | 124 | // addScaledSlice adds a scaled narray elementwise. 125 | // y = y + a * x 126 | // Assumptions the assembly can make: 127 | // y != nil, a != nil 128 | // len(x) == len(y) 129 | func addScaledSlice(y, x []float32, a float32) { 130 | for i, v := range x { 131 | y[i] += v * a 132 | } 133 | } 134 | 135 | // sqrtSlice will return math.Sqrt(values) of the array 136 | // Assumptions the assembly can make: 137 | // out != nil, a != nil 138 | // len(out) == len(a) 139 | func sqrtSlice(out, a []float32) { 140 | for i := 0; i < len(out); i++ { 141 | out[i] = float32(math.Sqrt(float64(a[i]))) 142 | } 143 | } 144 | 145 | // minSliceElement will the smallest value of the slice 146 | // Assumptions the assembly can make: 147 | // a != nil 148 | // len(a) > 0 149 | func minSliceElement(a []float32) float32 { 150 | min := a[0] 151 | for i := 1; i < len(a); i++ { 152 | if a[i] < min { 153 | min = a[i] 154 | } 155 | } 156 | return min 157 | } 158 | 159 | // maxSliceElement will the biggest value of the slice 160 | // Assumptions the assembly can make: 161 | // a != nil 162 | // len(a) > 0 163 | func maxSliceElement(a []float32) float32 { 164 | max := a[0] 165 | for i := 1; i < len(a); i++ { 166 | if a[i] > max { 167 | max = a[i] 168 | } 169 | } 170 | return max 171 | } 172 | 173 | // sliceSum will return the sum of all elements of the slice 174 | // Assumptions the assembly can make: 175 | // a != nil 176 | // len(a) >= 0 177 | func sliceSum(a []float32) float32 { 178 | sum := float32(0.0) 179 | for _, v := range a { 180 | sum += v 181 | } 182 | return sum 183 | } 184 | 185 | // absSlice will return math.Abs(values) of the array 186 | // Assumptions the assembly can make: 187 | // out != nil, a != nil 188 | // len(out) == len(a) 189 | func absSlice(out, a []float32) { 190 | for i, v := range a { 191 | out[i] = float32(math.Abs(float64(v))) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /na32/arrayfuncs_amd64.go: -------------------------------------------------------------------------------- 1 | // +build amd64 2 | 3 | package na32 4 | 5 | import "math" 6 | 7 | // These are function definitions for AMD64 optimized routines, 8 | // and fallback that can be used for performance testing. 9 | // See function documentation in arrayfuncs.go 10 | 11 | // approx 8x faster than Go 12 | func divSlice(out, a, b []float32) 13 | 14 | func divSliceGo(out, a, b []float32) { 15 | for i := 0; i < len(out); i++ { 16 | out[i] = a[i] / b[i] 17 | } 18 | } 19 | 20 | // approx 8x faster than Go 21 | func addSlice(out, a, b []float32) 22 | 23 | func addSliceGo(out, a, b []float32) { 24 | for i := 0; i < len(out); i++ { 25 | out[i] = a[i] + b[i] 26 | } 27 | } 28 | 29 | // approx 8x faster than Go 30 | func mulSlice(out, a, b []float32) 31 | 32 | func mulSliceGo(out, a, b []float32) { 33 | for i := 0; i < len(out); i++ { 34 | out[i] = a[i] * b[i] 35 | } 36 | } 37 | 38 | // approx 8x faster than Go 39 | func subSlice(out, a, b []float32) 40 | 41 | func subSliceGo(out, a, b []float32) { 42 | for i := 0; i < len(out); i++ { 43 | out[i] = a[i] - b[i] 44 | } 45 | } 46 | 47 | // approx 8x faster than Go 48 | func minSlice(out, a, b []float32) 49 | 50 | func minSliceGo(out, a, b []float32) { 51 | for i := 0; i < len(out); i++ { 52 | if a[i] < b[i] { 53 | out[i] = a[i] 54 | } else { 55 | out[i] = b[i] 56 | } 57 | } 58 | } 59 | 60 | // approx 8x faster than Go 61 | func maxSlice(out, a, b []float32) 62 | 63 | func maxSliceGo(out, a, b []float32) { 64 | for i := 0; i < len(out); i++ { 65 | if a[i] > b[i] { 66 | out[i] = a[i] 67 | } else { 68 | out[i] = b[i] 69 | } 70 | } 71 | } 72 | 73 | // approx Xx faster than Go 74 | func csignSlice(out, a, b []float32) 75 | 76 | func csignSliceGo(out, a, b []float32) { 77 | const sign = 1 << 31 78 | for i := 0; i < len(out); i++ { 79 | out[i] = math.Float32frombits(math.Float32bits(a[i])&^sign | math.Float32bits(b[i])&sign) 80 | } 81 | } 82 | 83 | // approx 4x faster than Go 84 | func cdivSlice(out, a []float32, c float32) 85 | 86 | func cdivSliceGo(out, a []float32, c float32) { 87 | for i := 0; i < len(out); i++ { 88 | out[i] = c / a[i] 89 | } 90 | } 91 | 92 | // approx 5x faster than Go 93 | func cmulSlice(out, a []float32, c float32) 94 | 95 | func cmulSliceGo(out, a []float32, c float32) { 96 | for i := 0; i < len(out); i++ { 97 | out[i] = c * a[i] 98 | } 99 | } 100 | 101 | // approx 5x faster than Go 102 | func caddSliceGo(out, a []float32, c float32) 103 | 104 | func caddSlice(out, a []float32, c float32) { 105 | for i := 0; i < len(out); i++ { 106 | out[i] = c + a[i] 107 | } 108 | } 109 | 110 | // approx 13x faster than Go 111 | func addScaledSlice(y, x []float32, a float32) 112 | 113 | func addScaledSliceGo(y, x []float32, a float32) { 114 | for i, v := range x { 115 | y[i] += v * a 116 | } 117 | } 118 | 119 | // approx 11x faster than Go 120 | func sqrtSlice(out, a []float32) 121 | 122 | func sqrtSliceGo(out, a []float32) { 123 | for i := 0; i < len(out); i++ { 124 | out[i] = float32(math.Sqrt(float64(a[i]))) 125 | } 126 | } 127 | 128 | // approx 18x faster than Go 129 | func absSlice(out, a []float32) 130 | 131 | func absSliceGo(out, a []float32) { 132 | for i, v := range a { 133 | out[i] = float32(math.Abs(float64(v))) 134 | } 135 | } 136 | 137 | // approx 15x faster than Go 138 | func minSliceElement(a []float32) float32 139 | 140 | func minSliceElementGo(a []float32) float32 { 141 | min := float32(math.MaxFloat32) 142 | for i := 0; i < len(a); i++ { 143 | if a[i] < min { 144 | min = a[i] 145 | } 146 | } 147 | return min 148 | } 149 | 150 | // approx 15x faster than Go 151 | func maxSliceElement(a []float32) float32 152 | 153 | func maxSliceElementGo(a []float32) float32 { 154 | max := float32(-math.MaxFloat32) 155 | for i := 0; i < len(a); i++ { 156 | if a[i] > max { 157 | max = a[i] 158 | } 159 | } 160 | return max 161 | } 162 | 163 | // approx 8x faster than Go 164 | func sliceSum(a []float32) float32 165 | 166 | func sliceSumGo(a []float32) float32 { 167 | sum := float32(0.0) 168 | for _, v := range a { 169 | sum += v 170 | } 171 | return sum 172 | } 173 | -------------------------------------------------------------------------------- /na32/arrayfuncs_amd64.s: -------------------------------------------------------------------------------- 1 | 2 | // func divSlice(out []float32, a []float32, b []float32) 3 | TEXT ·divSlice(SB), 7, $0 4 | MOVQ out+0(FP),SI // SI: &out 5 | MOVQ out_len+8(FP),DX // DX: len(out) 6 | MOVQ a+24(FP),R11 // R11: &a 7 | MOVQ b+48(FP),R9 // R9: &b 8 | MOVQ DX, R10 // R10: len(out) 9 | SHRQ $3, DX // DX: len(out) / 8 10 | ANDQ $7, R10 // R10: len(out) % 8 11 | CMPQ DX ,$0 12 | JEQ remain_div 13 | loopback_div: 14 | MOVUPS (R11),X0 15 | MOVUPS (R9),X1 16 | DIVPS X1,X0 17 | MOVUPS 16(R11),X2 18 | MOVUPS 16(R9),X3 19 | DIVPS X3,X2 20 | MOVUPS X0,(SI) 21 | MOVUPS X2,16(SI) 22 | ADDQ $32, R11 23 | ADDQ $32, R9 24 | ADDQ $32, SI 25 | SUBQ $1,DX 26 | JNZ loopback_div 27 | remain_div: 28 | CMPQ R10,$0 29 | JEQ done_div 30 | onemore_div: 31 | MOVSS (R11),X0 32 | MOVSS (R9),X1 33 | DIVSS X1,X0 34 | MOVSS X0,(SI) 35 | ADDQ $4, R11 36 | ADDQ $4, R9 37 | ADDQ $4, SI 38 | SUBQ $1, R10 39 | JNZ onemore_div 40 | done_div: 41 | RET 42 | 43 | 44 | 45 | // func mulSlice(out []float32, a []float32, b []float32) 46 | TEXT ·mulSlice(SB), 7, $0 47 | MOVQ out(FP),SI // SI: &out 48 | MOVQ out_len+8(FP),DX // DX: len(out) 49 | MOVQ a+24(FP),R11 // R11: &a 50 | MOVQ b+48(FP),R9 // R9: &b 51 | MOVQ DX, R10 // R10: len(out) 52 | SHRQ $3, DX // DX: len(out) / 8 53 | ANDQ $7, R10 // R10: len(out) % 8 54 | CMPQ DX ,$0 55 | JEQ remain_mul 56 | loopback_mul: 57 | MOVUPS (R11),X0 58 | MOVUPS (R9),X1 59 | MULPS X1,X0 60 | MOVUPS 16(R11),X2 61 | MOVUPS 16(R9),X3 62 | MULPS X3,X2 63 | MOVUPS X0,(SI) 64 | MOVUPS X2,16(SI) 65 | ADDQ $32, R11 66 | ADDQ $32, R9 67 | ADDQ $32, SI 68 | SUBQ $1,DX 69 | JNZ loopback_mul 70 | remain_mul: 71 | CMPQ R10,$0 72 | JEQ done_mul 73 | onemore_mul: 74 | MOVSS (R11),X0 75 | MOVSS (R9),X1 76 | MULSS X1,X0 77 | MOVSS X0,(SI) 78 | ADDQ $4, R11 79 | ADDQ $4, R9 80 | ADDQ $4, SI 81 | SUBQ $1, R10 82 | JNZ onemore_mul 83 | done_mul: 84 | RET 85 | 86 | 87 | // func addSlice(out []float32, a []float32, b []float32) 88 | TEXT ·addSlice(SB), 7, $0 89 | MOVQ out(FP),SI // SI: &out 90 | MOVQ out_len+8(FP),DX // DX: len(out) 91 | MOVQ a+24(FP),R11 // R11: &a 92 | MOVQ b+48(FP),R9 // R9: &b 93 | MOVQ DX, R10 // R10: len(out) 94 | SHRQ $3, DX // DX: len(out) / 8 95 | ANDQ $7, R10 // R10: len(out) % 8 96 | CMPQ DX ,$0 97 | JEQ remain_add 98 | loopback_add: 99 | MOVUPS (R11),X0 100 | MOVUPS (R9),X1 101 | ADDPS X1,X0 102 | MOVUPS 16(R11),X2 103 | MOVUPS 16(R9),X3 104 | ADDPS X3,X2 105 | MOVUPS X0,(SI) 106 | MOVUPS X2,16(SI) 107 | ADDQ $32, R11 108 | ADDQ $32, R9 109 | ADDQ $32, SI 110 | SUBQ $1,DX 111 | JNZ loopback_add 112 | remain_add: 113 | CMPQ R10,$0 114 | JEQ done_add 115 | onemore_add: 116 | MOVSS (R11),X0 117 | MOVSS (R9),X1 118 | ADDSS X1,X0 119 | MOVSS X0,(SI) 120 | ADDQ $4, R11 121 | ADDQ $4, R9 122 | ADDQ $4, SI 123 | SUBQ $1, R10 124 | JNZ onemore_add 125 | done_add: 126 | RET 127 | 128 | // func subSlice(out []float32, a []float32, b []float32) 129 | TEXT ·subSlice(SB), 7, $0 130 | MOVQ out+0(FP),SI // SI: &out 131 | MOVQ out_len+8(FP),DX // DX: len(out) 132 | MOVQ a+24(FP),R11 // R11: &a 133 | MOVQ b+48(FP),R9 // R9: &b 134 | MOVQ DX, R10 // R10: len(out) 135 | SHRQ $3, DX // DX: len(out) / 8 136 | ANDQ $7, R10 // R10: len(out) % 8 137 | CMPQ DX ,$0 138 | JEQ remain_sub 139 | loopback_sub: 140 | MOVUPS (R11),X0 141 | MOVUPS (R9),X1 142 | SUBPS X1,X0 143 | MOVUPS 16(R11),X2 144 | MOVUPS 16(R9),X3 145 | SUBPS X3,X2 146 | MOVUPS X0,(SI) 147 | MOVUPS X2,16(SI) 148 | ADDQ $32, R11 149 | ADDQ $32, R9 150 | ADDQ $32, SI 151 | SUBQ $1,DX 152 | JNZ loopback_sub 153 | remain_sub: 154 | CMPQ R10,$0 155 | JEQ done_sub 156 | onemore_sub: 157 | MOVSS (R11),X0 158 | MOVSS (R9),X1 159 | SUBSS X1,X0 160 | MOVSS X0,(SI) 161 | ADDQ $4, R11 162 | ADDQ $4, R9 163 | ADDQ $4, SI 164 | SUBQ $1, R10 165 | JNZ onemore_sub 166 | done_sub: 167 | RET 168 | 169 | // func minSlice(out []float32, a []float32, b []float32) 170 | TEXT ·minSlice(SB), 7, $0 171 | MOVQ out(FP),SI // SI: &out 172 | MOVQ out_len+8(FP),DX // DX: len(out) 173 | MOVQ a+24(FP),R11 // R11: &a 174 | MOVQ b+48(FP),R9 // R9: &b 175 | MOVQ DX, R10 // R10: len(out) 176 | SHRQ $3, DX // DX: len(out) / 8 177 | ANDQ $7, R10 // R10: len(out) % 8 178 | CMPQ DX ,$0 179 | JEQ remain_min 180 | loopback_min: 181 | MOVUPS (R11),X0 182 | MOVUPS (R9),X1 183 | MINPS X1,X0 184 | MOVUPS 16(R11),X2 185 | MOVUPS 16(R9),X3 186 | MINPS X3,X2 187 | MOVUPS X0,(SI) 188 | MOVUPS X2,16(SI) 189 | ADDQ $32, R11 190 | ADDQ $32, R9 191 | ADDQ $32, SI 192 | SUBQ $1,DX 193 | JNZ loopback_min 194 | remain_min: 195 | CMPQ R10,$0 196 | JEQ done_min 197 | onemore_min: 198 | MOVSS (R11),X0 199 | MOVSS (R9),X1 200 | MINSS X1,X0 201 | MOVSS X0,(SI) 202 | ADDQ $4, R11 203 | ADDQ $4, R9 204 | ADDQ $4, SI 205 | SUBQ $1, R10 206 | JNZ onemore_min 207 | done_min: 208 | RET 209 | 210 | // func maxSlice(out []float32, a []float32, b []float32) 211 | TEXT ·maxSlice(SB), 7, $0 212 | MOVQ out(FP),SI // SI: &out 213 | MOVQ out_len+8(FP),DX // DX: len(out) 214 | MOVQ a+24(FP),R11 // R11: &a 215 | MOVQ b+48(FP),R9 // R9: &b 216 | MOVQ DX, R10 // R10: len(out) 217 | SHRQ $3, DX // DX: len(out) / 8 218 | ANDQ $7, R10 // R10: len(out) % 8 219 | CMPQ DX ,$0 220 | JEQ remain_max 221 | loopback_max: 222 | MOVUPS (R11),X0 223 | MOVUPS (R9),X1 224 | MAXPS X1,X0 225 | MOVUPS 16(R11),X2 226 | MOVUPS 16(R9),X3 227 | MAXPS X3,X2 228 | MOVUPS X0,(SI) 229 | MOVUPS X2,16(SI) 230 | ADDQ $32, R11 231 | ADDQ $32, R9 232 | ADDQ $32, SI 233 | SUBQ $1,DX 234 | JNZ loopback_max 235 | remain_max: 236 | CMPQ R10,$0 237 | JEQ done_max 238 | onemore_max: 239 | MOVSS (R11),X0 240 | MOVSS (R9),X1 241 | MAXSS X1,X0 242 | MOVSS X0,(SI) 243 | ADDQ $4, R11 244 | ADDQ $4, R9 245 | ADDQ $4, SI 246 | SUBQ $1, R10 247 | JNZ onemore_max 248 | done_max: 249 | RET 250 | 251 | 252 | // func csignSlice(out []float64, a []float64, b []float64) 253 | TEXT ·csignSlice(SB), 7, $0 254 | MOVQ out(FP),SI // SI: &out 255 | MOVQ out_len+8(FP),DX // DX: len(out) 256 | MOVQ a+24(FP),R11 // R11: &a 257 | MOVQ b+48(FP),R9 // R9: &b 258 | MOVQ DX, R10 // R10: len(out) 259 | MOVQ $(1<<63), BX 260 | MOVQ BX, X4 // X4: Sign 261 | SHUFPS $0, X4, X4 262 | SHRQ $3, DX // DX: len(out) / 8 263 | ANDQ $7, R10 // R10: len(out) % 8 264 | CMPQ DX ,$0 265 | JEQ remain_csign 266 | loopback_csign: 267 | MOVAPS X4, X5 268 | MOVAPS X4, X6 269 | MOVUPS (R11),X0 270 | MOVUPS (R9),X1 271 | MOVUPS 16(R11),X2 272 | MOVUPS 16(R9),X3 273 | ANDNPS X0, X5 274 | ANDPS X4, X1 275 | ORPS X5, X1 276 | 277 | ANDNPS X2, X6 278 | ANDPS X4, X3 279 | ORPS X6, X3 280 | MOVUPS X1,(SI) 281 | MOVUPS X3,16(SI) 282 | ADDQ $32, R11 283 | ADDQ $32, R9 284 | ADDQ $32, SI 285 | SUBQ $1,DX 286 | JNZ loopback_csign 287 | remain_csign: 288 | CMPQ R10,$0 289 | JEQ done_csign 290 | onemore_csign: 291 | MOVSS X4, X5 292 | MOVSS (R11),X0 293 | MOVSS (R9),X1 294 | ANDNPS X0, X5 295 | ANDPS X4, X1 296 | ORPS X5, X1 297 | MOVSS X1,(SI) 298 | ADDQ $4, R11 299 | ADDQ $4, R9 300 | ADDQ $4, SI 301 | SUBQ $1, R10 302 | JNZ onemore_csign 303 | done_csign: 304 | RET 305 | 306 | // func cdivSlice(out []float32, a []float32, c float32) 307 | TEXT ·cdivSlice(SB), 7, $0 308 | MOVQ out(FP),SI // SI: &out 309 | MOVQ out_len+8(FP),DX // DX: len(out) 310 | MOVQ a+24(FP),R11 // R11: &a 311 | MOVSS c+48(FP),X4 // X4: c 312 | MOVQ DX, R10 // R10: len(out) 313 | SHRQ $3, DX // DX: len(out) / 8 314 | ANDQ $7, R10 // R10: len(out) % 8 315 | CMPQ DX ,$0 316 | JEQ remain_cdiv 317 | SHUFPS $0, X4, X4 318 | loopback_cdiv: 319 | MOVAPS X4, X1 320 | MOVAPS X4, X3 321 | MOVUPS (R11),X0 322 | DIVPS X0,X1 323 | MOVUPS 16(R11),X2 324 | DIVPS X2,X3 325 | MOVUPS X1,(SI) 326 | MOVUPS X3,16(SI) 327 | ADDQ $32, R11 328 | ADDQ $32, SI 329 | SUBQ $1,DX 330 | JNZ loopback_cdiv 331 | remain_cdiv: 332 | CMPQ R10,$0 333 | JEQ done_cdiv 334 | onemore_cdiv: 335 | MOVAPS X4, X1 336 | MOVSS (R11),X0 337 | DIVSS X0,X1 338 | MOVSS X1,(SI) 339 | ADDQ $4, R11 340 | ADDQ $4, SI 341 | SUBQ $1, R10 342 | JNZ onemore_cdiv 343 | done_cdiv: 344 | RET 345 | 346 | 347 | // func cmulSlice(out []float32, a []float32, c float32) 348 | TEXT ·cmulSlice(SB), 7, $0 349 | MOVQ out(FP),SI // SI: &out 350 | MOVQ out_len+8(FP),DX // DX: len(out) 351 | MOVQ a+24(FP),R11 // R11: &a 352 | MOVSS c+48(FP),X4 // X4: c 353 | MOVQ DX, R10 // R10: len(out) 354 | SHRQ $3, DX // DX: len(out) / 8 355 | ANDQ $7, R10 // R10: len(out) % 8 356 | CMPQ DX ,$0 357 | JEQ remain_cmul 358 | SHUFPS $0, X4, X4 359 | loopback_cmul: 360 | MOVUPS (R11),X0 361 | MULPS X4,X0 362 | MOVUPS 16(R11),X2 363 | MULPS X4,X2 364 | MOVUPS X0,(SI) 365 | MOVUPS X2,16(SI) 366 | ADDQ $32, R11 367 | ADDQ $32, SI 368 | SUBQ $1,DX 369 | JNZ loopback_cmul 370 | remain_cmul: 371 | CMPQ R10,$0 372 | JEQ done_cmul 373 | onemore_cmul: 374 | MOVSS (R11),X0 375 | MULSS X4,X0 376 | MOVSS X0,(SI) 377 | ADDQ $4, R11 378 | ADDQ $4, SI 379 | SUBQ $1, R10 380 | JNZ onemore_cmul 381 | done_cmul: 382 | RET 383 | 384 | 385 | // func caddSlice(out []float32, a []float32, c float32) 386 | TEXT ·caddSlice(SB), 7, $0 387 | MOVQ out(FP),SI // SI: &out 388 | MOVQ out_len+8(FP),DX // DX: len(out) 389 | MOVQ a+24(FP),R11 // R11: &a 390 | MOVSS c+48(FP),X4 // X4: c 391 | MOVQ DX, R10 // R10: len(out) 392 | SHRQ $3, DX // DX: len(out) / 8 393 | ANDQ $7, R10 // R10: len(out) % 8 394 | CMPQ DX ,$0 395 | JEQ remain_cadd 396 | SHUFPS $0, X4, X4 397 | loopback_cadd: 398 | MOVUPS (R11),X0 399 | ADDPS X4,X0 400 | MOVUPS 16(R11),X2 401 | ADDPS X4,X2 402 | MOVUPS X0,(SI) 403 | MOVUPS X2,16(SI) 404 | ADDQ $32, R11 405 | ADDQ $32, SI 406 | SUBQ $1,DX 407 | JNZ loopback_cadd 408 | remain_cadd: 409 | CMPQ R10,$0 410 | JEQ done_cadd 411 | onemore_cadd: 412 | MOVSS (R11),X0 413 | ADDSS X4,X0 414 | MOVSS X0,(SI) 415 | ADDQ $4, R11 416 | ADDQ $4, SI 417 | SUBQ $1, R10 418 | JNZ onemore_cadd 419 | done_cadd: 420 | RET 421 | 422 | 423 | // func addScaledSlice(y []float32, x []float32, a float32) 424 | TEXT ·addScaledSlice(SB), 7, $0 425 | MOVQ y(FP),SI // SI: &y 426 | MOVQ y_len+8(FP),DX // DX: len(y) 427 | MOVQ x+24(FP),R11 // R11: &x 428 | MOVSS a+48(FP),X4 // X4: a 429 | MOVQ DX, R10 // R10: len(y) 430 | SHRQ $3, DX // DX: len(y) / 8 431 | ANDQ $7, R10 // R10: len(y) % 8 432 | CMPQ DX ,$0 433 | JEQ remain_madd 434 | SHUFPS $0, X4, X4 435 | loopback_madd: 436 | MOVUPS (R11),X0 437 | MOVUPS (SI), X5 438 | MULPS X4, X0 439 | ADDPS X5, X0 440 | MOVUPS 16(R11),X2 441 | MOVUPS 16(SI), X6 442 | MULPS X4, X2 443 | ADDPS X6, X2 444 | MOVUPS X0,(SI) 445 | MOVUPS X2,16(SI) 446 | ADDQ $32, R11 447 | ADDQ $32, SI 448 | SUBQ $1,DX 449 | JNZ loopback_madd 450 | remain_madd: 451 | CMPQ R10,$0 452 | JEQ done_madd 453 | onemore_madd: 454 | MOVSS (R11),X0 455 | MOVSS (SI),X1 456 | MULSS X4,X0 457 | ADDSS X1,X0 458 | MOVSS X0,(SI) 459 | ADDQ $4, R11 460 | ADDQ $4, SI 461 | SUBQ $1, R10 462 | JNZ onemore_madd 463 | done_madd: 464 | RET 465 | 466 | // func sqrtSlice(out []float32, a []float32) 467 | TEXT ·sqrtSlice(SB), 7, $0 468 | MOVQ out(FP),SI // SI: &out 469 | MOVQ out_len+8(FP),DX // DX: len(out) 470 | MOVQ a+24(FP),R11 // R11: &a 471 | MOVQ DX, R10 // R10: len(out) 472 | SHRQ $3, DX // DX: len(out) / 8 473 | ANDQ $7, R10 // R10: len(out) % 8 474 | CMPQ DX ,$0 475 | JEQ remain_sqrt 476 | loopback_sqrt: 477 | MOVUPS (R11),X0 478 | SQRTPS X0,X0 479 | MOVUPS 16(R11),X2 480 | SQRTPS X2,X2 481 | MOVUPS X0,(SI) 482 | MOVUPS X2,16(SI) 483 | ADDQ $32, R11 484 | ADDQ $32, SI 485 | SUBQ $1,DX 486 | JNZ loopback_sqrt 487 | remain_sqrt: 488 | CMPQ R10,$0 489 | JEQ done_sqrt 490 | onemore_sqrt: 491 | MOVSS (R11),X0 492 | SQRTSS X0,X0 493 | MOVSS X0,(SI) 494 | ADDQ $4, R11 495 | ADDQ $4, SI 496 | SUBQ $1, R10 497 | JNZ onemore_sqrt 498 | done_sqrt: 499 | RET 500 | 501 | // func absSlice(out []float32, a []float32) 502 | TEXT ·absSlice(SB), 7, $0 503 | MOVQ out(FP),SI // SI: &out 504 | MOVQ out_len+8(FP),DX // DX: len(out) 505 | MOVQ a+24(FP),R11 // R11: &a 506 | MOVQ $(1<<31), BX 507 | MOVQ BX, X5 // X1: Sign 508 | SHUFPS $0, X5, X5 509 | MOVQ DX, R10 // R10: len(out) 510 | SHRQ $3, DX // DX: len(out) / 8 511 | ANDQ $7, R10 // R10: len(out) % 8 512 | CMPQ DX ,$0 513 | JEQ remain_abs 514 | loopback_abs: 515 | MOVAPS X5, X3 516 | MOVAPS X5, X4 517 | MOVUPS (R11),X0 518 | ANDNPS X0, X3 519 | MOVUPS 16(R11),X1 520 | ANDNPS X1, X4 521 | MOVUPS X3,(SI) 522 | MOVUPS X4,16(SI) 523 | ADDQ $32, R11 524 | ADDQ $32, SI 525 | SUBQ $1,DX 526 | JNZ loopback_abs 527 | remain_abs: 528 | CMPQ R10,$0 529 | JEQ done_abs 530 | onemore_abs: 531 | MOVAPS X5, X1 532 | MOVSS (R11),X0 533 | ANDNPS X0, X1 534 | MOVSS X1,(SI) 535 | ADDQ $4, R11 536 | ADDQ $4, SI 537 | SUBQ $1, R10 538 | JNZ onemore_abs 539 | done_abs: 540 | RET 541 | 542 | // func minSliceElement(a []float32) float32 543 | TEXT ·minSliceElement(SB), 7, $0 544 | MOVQ a(FP),SI // SI: &a 545 | MOVQ a_len+8(FP),DX // DX: len(a) 546 | MOVSS (SI), X0 // Initial value 547 | ADDQ $4, SI 548 | SUBQ $1, DX 549 | 550 | SHUFPS $0, X0, X0 551 | MOVQ DX, R10 // R10: len(out) -1 552 | SHRQ $3, DX // DX: (len(out) - 1) / 8 553 | ANDQ $7, R10 // R10: (len(out) -1 ) % 8 554 | MOVAPS X0, X1 555 | CMPQ DX ,$0 556 | JEQ remain_min_e 557 | next_min_e: 558 | MOVUPS (SI), X2 559 | MOVUPS 16(SI), X3 560 | MINPS X2, X0 561 | MINPS X3, X1 562 | ADDQ $32, SI 563 | SUBQ $1, DX 564 | JNZ next_min_e 565 | CMPQ R10, $0 566 | JZ done_min_e 567 | remain_min_e: 568 | MOVSS (SI), X2 569 | MINSS X2, X0 570 | ADDQ $4, SI 571 | SUBQ $1, R10 572 | JNZ remain_min_e 573 | done_min_e: 574 | MINPS X1, X0 575 | MOVAPS X0, X1 576 | MOVAPS X0, X2 577 | MOVAPS X0, X3 578 | SHUFPS $1, X1, X1 // Put Element 1 into lower X1 579 | SHUFPS $2, X2, X2 // Put Element 2 into lower X2 580 | SHUFPS $3, X3, X3 // Put Element 3 into lower X3 581 | 582 | MINSS X1, X0 583 | MINSS X3, X2 584 | MINSS X2, X0 585 | MOVSS X0, ret+24(FP) 586 | RET 587 | 588 | 589 | // func maxSliceElement(a []float32) float32 590 | TEXT ·maxSliceElement(SB), 7, $0 591 | MOVQ a(FP),SI // SI: &a 592 | MOVQ a_len+8(FP),DX // DX: len(a) 593 | MOVSS (SI), X0 // Initial value 594 | ADDQ $4, SI 595 | SUBQ $1, DX 596 | 597 | SHUFPS $0, X0, X0 598 | MOVQ DX, R10 // R10: len(out) -1 599 | SHRQ $3, DX // DX: (len(out) - 1) / 8 600 | ANDQ $7, R10 // R10: (len(out) -1 ) % 8 601 | MOVAPS X0, X1 602 | CMPQ DX ,$0 603 | JEQ remain_max_e 604 | next_max_e: 605 | MOVUPS (SI), X2 606 | MOVUPS 16(SI), X3 607 | MAXPS X2, X0 608 | MAXPS X3, X1 609 | ADDQ $32, SI 610 | SUBQ $1, DX 611 | JNZ next_max_e 612 | CMPQ R10, $0 613 | JZ done_max_e 614 | remain_max_e: 615 | MOVSS (SI), X2 616 | MAXSS X2, X0 617 | ADDQ $4, SI 618 | SUBQ $1, R10 619 | JNZ remain_max_e 620 | done_max_e: 621 | MAXPS X1, X0 622 | MOVAPS X0, X1 623 | MOVAPS X0, X2 624 | MOVAPS X0, X3 625 | SHUFPS $1, X1, X1 // Put Element 1 into lower X1 626 | SHUFPS $2, X2, X2 // Put Element 2 into lower X2 627 | SHUFPS $3, X3, X3 // Put Element 3 into lower X3 628 | 629 | MAXSS X1, X0 630 | MAXSS X3, X2 631 | MAXSS X2, X0 632 | MOVSS X0, ret+24(FP) 633 | RET 634 | 635 | 636 | 637 | // func sliceSum(a []float32) float32 638 | TEXT ·sliceSum(SB), 7, $0 639 | MOVQ a(FP),SI // SI: &a 640 | MOVQ a_len+8(FP),DX // DX: len(a) 641 | XORPS X0, X0 // Sum 1 642 | XORPS X1, X1 // Sum 2 643 | 644 | MOVQ DX, R10 // R10: len(out) 645 | SHRQ $3, DX // DX: (len(out)) / 8 646 | ANDQ $7, R10 // R10: (len(out)) % 8 647 | CMPQ DX ,$0 648 | JEQ remain_sum 649 | next_sum: 650 | MOVUPS (SI), X2 651 | MOVUPS 16(SI), X3 652 | ADDPS X2, X0 653 | ADDPS X3, X1 654 | ADDQ $32, SI 655 | SUBQ $1, DX 656 | JNZ next_sum 657 | CMPQ R10, $0 658 | JZ done_sum 659 | remain_sum: 660 | MOVSS (SI), X2 661 | ADDSS X2, X0 662 | ADDQ $4, SI 663 | SUBQ $1, R10 664 | JNZ remain_sum 665 | done_sum: 666 | ADDPS X1, X0 667 | MOVAPS X0, X1 668 | MOVAPS X0, X2 669 | MOVAPS X0, X3 670 | SHUFPS $1, X1, X1 // Put Element 1 into lower X1 671 | SHUFPS $2, X2, X2 // Put Element 2 into lower X2 672 | SHUFPS $3, X3, X3 // Put Element 3 into lower X3 673 | 674 | ADDSS X1, X0 675 | ADDSS X3, X2 676 | ADDSS X2, X0 677 | 678 | MOVSS X0, ret+24(FP) 679 | RET 680 | -------------------------------------------------------------------------------- /na32/gonum.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package na32 9 | 10 | // Matrix is as an NArray of rank 2 that satisfies the gonum Matrix interfaces. 11 | type Matrix NArray 12 | 13 | // Vector is as an NArray of rank 1 that satisfies the gonum Vectorer interface. 14 | type Vector NArray 15 | 16 | // Matrix creates a subarray of rank 2. 17 | // Equivalent to SubArray but restricted to the case where the 18 | // resulting subarray has rank=2. (It will panic otherwise.) 19 | // See SubArray for details. 20 | func (na *NArray) Matrix(query ...int) *Matrix { 21 | 22 | mat := na.SubArray(query...) 23 | if len(mat.Shape) != 2 { 24 | panic("matrix must have rank equal two") 25 | } 26 | return (*Matrix)(mat) 27 | } 28 | 29 | // Dims returns the dimensions of a Matrix. 30 | func (mat *Matrix) Dims() (r, c int) { 31 | na := (*NArray)(mat) 32 | return na.Shape[0], na.Shape[1] 33 | } 34 | 35 | // At returns the value of a matrix element at (r, c). It will panic if r or c are 36 | // out of bounds for the matrix. 37 | func (mat *Matrix) At(r, c int) float32 { 38 | na := (*NArray)(mat) 39 | return na.At(r, c) 40 | } 41 | 42 | // Set alters the matrix element at (r, c) to v. It will panic if r or c are out of 43 | // bounds for the matrix. 44 | func (mat *Matrix) Set(r, c int, v float32) { 45 | na := (*NArray)(mat) 46 | na.Set(v, r, c) 47 | } 48 | 49 | // String returns vector as a printable string. 50 | func (mat *Matrix) String() string { 51 | na := (*NArray)(mat) 52 | return na.String() 53 | } 54 | 55 | // Vector creates a subarray of rank 1. 56 | // Equivalent to SubArray but restricted to the case where the 57 | // resulting subarray has rank=1. (It will panic otherwise.) 58 | // See SubArray for details. 59 | // 60 | // Example, given a 5x10 matrix (rank=2), return the vector 61 | // of dim 10 for row idx=3: 62 | // 63 | // x := New(5,10) 64 | // y := x.Vector(3,-1) 65 | // // y = {x_30, x_31, ... , x_39} 66 | func (na *NArray) Vector(query ...int) *Vector { 67 | 68 | vec := na.SubArray(query...) 69 | if len(vec.Shape) != 1 { 70 | panic("vector must have rank equal one") 71 | } 72 | return (*Vector)(vec) 73 | } 74 | 75 | // Row returns a slice of float32 for the row specified. It will panic if the index 76 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 77 | // returned, if it is not nil the number of elements copied will be the minimum of the 78 | // length of the slice and the number of columns in the matrix. 79 | func (mat *Matrix) Row(dst []float32, i int) []float32 { 80 | _, ncols := mat.Dims() 81 | if dst == nil { 82 | dst = make([]float32, ncols, ncols) 83 | } 84 | for j, _ := range dst { 85 | dst[j] = mat.At(i, j) 86 | } 87 | return dst 88 | } 89 | 90 | // Col returns a slice of float32 for the column specified. It will panic if the index 91 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 92 | // returned, if it is not nil the number of elements copied will be the minimum of the 93 | // length of the slice and the number of rows in the matrix. 94 | func (mat *Matrix) Col(dst []float32, j int) []float32 { 95 | nrows, _ := mat.Dims() 96 | if dst == nil { 97 | dst = make([]float32, nrows, nrows) 98 | } 99 | for i, _ := range dst { 100 | dst[i] = mat.At(i, j) 101 | } 102 | return dst 103 | } 104 | 105 | // SetRow sets the values of the specified row to the values held in a slice of float32. 106 | // It will panic if the index is out of bounds. The number of elements copied is 107 | // returned and will be the minimum of the length of the slice and the number of columns 108 | // in the matrix. 109 | func (mat *Matrix) SetRow(i int, src []float32) int { 110 | 111 | numCopied := len(src) 112 | if len(src) > mat.Shape[1] { 113 | numCopied = mat.Shape[1] 114 | } 115 | for j := 0; j < numCopied; j++ { 116 | mat.Set(i, j, src[j]) 117 | } 118 | return numCopied 119 | } 120 | 121 | // SetCol sets the values of the specified column to the values held in a slice of float32. 122 | // It will panic if the index is out of bounds. The number of elements copied is 123 | // returned and will be the minimum of the length of the slice and the number of rows 124 | // in the matrix. 125 | func (mat *Matrix) SetCol(j int, src []float32) int { 126 | 127 | numCopied := len(src) 128 | if len(src) > mat.Shape[0] { 129 | numCopied = mat.Shape[0] 130 | } 131 | for i := 0; i < numCopied; i++ { 132 | mat.Set(i, j, src[i]) 133 | } 134 | return numCopied 135 | } 136 | 137 | // TODO finish implementing gonum interfaces. 138 | -------------------------------------------------------------------------------- /na32/gonum_test.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package na32 9 | 10 | import "testing" 11 | 12 | func TestMatrix(t *testing.T) { 13 | 14 | mat := na234.Matrix(0, -1, -1) 15 | t.Log(mat) 16 | 17 | row0 := mat.Row(nil, 0) 18 | row1 := mat.Row(nil, 1) 19 | row2 := mat.Row(nil, 2) 20 | col0 := mat.Col(nil, 0) 21 | col1 := mat.Col(nil, 1) 22 | col2 := mat.Col(nil, 2) 23 | col3 := mat.Col(nil, 3) 24 | 25 | t.Log(row0) 26 | t.Log(row1) 27 | t.Log(row2) 28 | t.Log(col0) 29 | t.Log(col1) 30 | t.Log(col2) 31 | t.Log(col3) 32 | 33 | na := na234.SubArray(1, 2, -1) 34 | vec := na234.Vector(1, 2, -1) 35 | if !EqualValues(na, (*NArray)(vec), 0) { 36 | t.Fatalf("expected same values") 37 | } 38 | 39 | na = na234.SubArray(-1, 2, -1) 40 | mat = na234.Matrix(-1, 2, -1) 41 | if !EqualValues(na, (*NArray)(mat), 0) { 42 | t.Fatalf("expected same values") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /na32/math_gen.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | // more info at github.com/akualab/narray 3 | 4 | package na32 5 | 6 | import "math" 7 | 8 | // Acosh applies math.Acosh() elementwise to a multidimensional array. 9 | // See math package in standard lib for details. 10 | // 11 | // If 'out' is nil a new array is created. 12 | // Will panic if 'out' and 'in' shapes don't match. 13 | func Acosh(out, in *NArray) *NArray { 14 | if out == nil { 15 | out = New(in.Shape...) 16 | } else if !EqualShape(out, in) { 17 | panic("Acosh:narrays must have equal shape.") 18 | } 19 | for k, v := range in.Data { 20 | out.Data[k] = float32(math.Acosh(float64(v))) 21 | } 22 | return out 23 | } 24 | 25 | // Asin applies math.Asin() elementwise to a multidimensional array. 26 | // See math package in standard lib for details. 27 | // 28 | // If 'out' is nil a new array is created. 29 | // Will panic if 'out' and 'in' shapes don't match. 30 | func Asin(out, in *NArray) *NArray { 31 | if out == nil { 32 | out = New(in.Shape...) 33 | } else if !EqualShape(out, in) { 34 | panic("Asin:narrays must have equal shape.") 35 | } 36 | for k, v := range in.Data { 37 | out.Data[k] = float32(math.Asin(float64(v))) 38 | } 39 | return out 40 | } 41 | 42 | // Acos applies math.Acos() elementwise to a multidimensional array. 43 | // See math package in standard lib for details. 44 | // 45 | // If 'out' is nil a new array is created. 46 | // Will panic if 'out' and 'in' shapes don't match. 47 | func Acos(out, in *NArray) *NArray { 48 | if out == nil { 49 | out = New(in.Shape...) 50 | } else if !EqualShape(out, in) { 51 | panic("Acos:narrays must have equal shape.") 52 | } 53 | for k, v := range in.Data { 54 | out.Data[k] = float32(math.Acos(float64(v))) 55 | } 56 | return out 57 | } 58 | 59 | // Asinh applies math.Asinh() elementwise to a multidimensional array. 60 | // See math package in standard lib for details. 61 | // 62 | // If 'out' is nil a new array is created. 63 | // Will panic if 'out' and 'in' shapes don't match. 64 | func Asinh(out, in *NArray) *NArray { 65 | if out == nil { 66 | out = New(in.Shape...) 67 | } else if !EqualShape(out, in) { 68 | panic("Asinh:narrays must have equal shape.") 69 | } 70 | for k, v := range in.Data { 71 | out.Data[k] = float32(math.Asinh(float64(v))) 72 | } 73 | return out 74 | } 75 | 76 | // Atan applies math.Atan() elementwise to a multidimensional array. 77 | // See math package in standard lib for details. 78 | // 79 | // If 'out' is nil a new array is created. 80 | // Will panic if 'out' and 'in' shapes don't match. 81 | func Atan(out, in *NArray) *NArray { 82 | if out == nil { 83 | out = New(in.Shape...) 84 | } else if !EqualShape(out, in) { 85 | panic("Atan:narrays must have equal shape.") 86 | } 87 | for k, v := range in.Data { 88 | out.Data[k] = float32(math.Atan(float64(v))) 89 | } 90 | return out 91 | } 92 | 93 | // Atanh applies math.Atanh() elementwise to a multidimensional array. 94 | // See math package in standard lib for details. 95 | // 96 | // If 'out' is nil a new array is created. 97 | // Will panic if 'out' and 'in' shapes don't match. 98 | func Atanh(out, in *NArray) *NArray { 99 | if out == nil { 100 | out = New(in.Shape...) 101 | } else if !EqualShape(out, in) { 102 | panic("Atanh:narrays must have equal shape.") 103 | } 104 | for k, v := range in.Data { 105 | out.Data[k] = float32(math.Atanh(float64(v))) 106 | } 107 | return out 108 | } 109 | 110 | // Cbrt applies math.Cbrt() elementwise to a multidimensional array. 111 | // See math package in standard lib for details. 112 | // 113 | // If 'out' is nil a new array is created. 114 | // Will panic if 'out' and 'in' shapes don't match. 115 | func Cbrt(out, in *NArray) *NArray { 116 | if out == nil { 117 | out = New(in.Shape...) 118 | } else if !EqualShape(out, in) { 119 | panic("Cbrt:narrays must have equal shape.") 120 | } 121 | for k, v := range in.Data { 122 | out.Data[k] = float32(math.Cbrt(float64(v))) 123 | } 124 | return out 125 | } 126 | 127 | // Erf applies math.Erf() elementwise to a multidimensional array. 128 | // See math package in standard lib for details. 129 | // 130 | // If 'out' is nil a new array is created. 131 | // Will panic if 'out' and 'in' shapes don't match. 132 | func Erf(out, in *NArray) *NArray { 133 | if out == nil { 134 | out = New(in.Shape...) 135 | } else if !EqualShape(out, in) { 136 | panic("Erf:narrays must have equal shape.") 137 | } 138 | for k, v := range in.Data { 139 | out.Data[k] = float32(math.Erf(float64(v))) 140 | } 141 | return out 142 | } 143 | 144 | // Erfc applies math.Erfc() elementwise to a multidimensional array. 145 | // See math package in standard lib for details. 146 | // 147 | // If 'out' is nil a new array is created. 148 | // Will panic if 'out' and 'in' shapes don't match. 149 | func Erfc(out, in *NArray) *NArray { 150 | if out == nil { 151 | out = New(in.Shape...) 152 | } else if !EqualShape(out, in) { 153 | panic("Erfc:narrays must have equal shape.") 154 | } 155 | for k, v := range in.Data { 156 | out.Data[k] = float32(math.Erfc(float64(v))) 157 | } 158 | return out 159 | } 160 | 161 | // Exp applies math.Exp() elementwise to a multidimensional array. 162 | // See math package in standard lib for details. 163 | // 164 | // If 'out' is nil a new array is created. 165 | // Will panic if 'out' and 'in' shapes don't match. 166 | func Exp(out, in *NArray) *NArray { 167 | if out == nil { 168 | out = New(in.Shape...) 169 | } else if !EqualShape(out, in) { 170 | panic("Exp:narrays must have equal shape.") 171 | } 172 | for k, v := range in.Data { 173 | out.Data[k] = float32(math.Exp(float64(v))) 174 | } 175 | return out 176 | } 177 | 178 | // Exp2 applies math.Exp2() elementwise to a multidimensional array. 179 | // See math package in standard lib for details. 180 | // 181 | // If 'out' is nil a new array is created. 182 | // Will panic if 'out' and 'in' shapes don't match. 183 | func Exp2(out, in *NArray) *NArray { 184 | if out == nil { 185 | out = New(in.Shape...) 186 | } else if !EqualShape(out, in) { 187 | panic("Exp2:narrays must have equal shape.") 188 | } 189 | for k, v := range in.Data { 190 | out.Data[k] = float32(math.Exp2(float64(v))) 191 | } 192 | return out 193 | } 194 | 195 | // Expm1 applies math.Expm1() elementwise to a multidimensional array. 196 | // See math package in standard lib for details. 197 | // 198 | // If 'out' is nil a new array is created. 199 | // Will panic if 'out' and 'in' shapes don't match. 200 | func Expm1(out, in *NArray) *NArray { 201 | if out == nil { 202 | out = New(in.Shape...) 203 | } else if !EqualShape(out, in) { 204 | panic("Expm1:narrays must have equal shape.") 205 | } 206 | for k, v := range in.Data { 207 | out.Data[k] = float32(math.Expm1(float64(v))) 208 | } 209 | return out 210 | } 211 | 212 | // Floor applies math.Floor() elementwise to a multidimensional array. 213 | // See math package in standard lib for details. 214 | // 215 | // If 'out' is nil a new array is created. 216 | // Will panic if 'out' and 'in' shapes don't match. 217 | func Floor(out, in *NArray) *NArray { 218 | if out == nil { 219 | out = New(in.Shape...) 220 | } else if !EqualShape(out, in) { 221 | panic("Floor:narrays must have equal shape.") 222 | } 223 | for k, v := range in.Data { 224 | out.Data[k] = float32(math.Floor(float64(v))) 225 | } 226 | return out 227 | } 228 | 229 | // Ceil applies math.Ceil() elementwise to a multidimensional array. 230 | // See math package in standard lib for details. 231 | // 232 | // If 'out' is nil a new array is created. 233 | // Will panic if 'out' and 'in' shapes don't match. 234 | func Ceil(out, in *NArray) *NArray { 235 | if out == nil { 236 | out = New(in.Shape...) 237 | } else if !EqualShape(out, in) { 238 | panic("Ceil:narrays must have equal shape.") 239 | } 240 | for k, v := range in.Data { 241 | out.Data[k] = float32(math.Ceil(float64(v))) 242 | } 243 | return out 244 | } 245 | 246 | // Trunc applies math.Trunc() elementwise to a multidimensional array. 247 | // See math package in standard lib for details. 248 | // 249 | // If 'out' is nil a new array is created. 250 | // Will panic if 'out' and 'in' shapes don't match. 251 | func Trunc(out, in *NArray) *NArray { 252 | if out == nil { 253 | out = New(in.Shape...) 254 | } else if !EqualShape(out, in) { 255 | panic("Trunc:narrays must have equal shape.") 256 | } 257 | for k, v := range in.Data { 258 | out.Data[k] = float32(math.Trunc(float64(v))) 259 | } 260 | return out 261 | } 262 | 263 | // Gamma applies math.Gamma() elementwise to a multidimensional array. 264 | // See math package in standard lib for details. 265 | // 266 | // If 'out' is nil a new array is created. 267 | // Will panic if 'out' and 'in' shapes don't match. 268 | func Gamma(out, in *NArray) *NArray { 269 | if out == nil { 270 | out = New(in.Shape...) 271 | } else if !EqualShape(out, in) { 272 | panic("Gamma:narrays must have equal shape.") 273 | } 274 | for k, v := range in.Data { 275 | out.Data[k] = float32(math.Gamma(float64(v))) 276 | } 277 | return out 278 | } 279 | 280 | // J0 applies math.J0() elementwise to a multidimensional array. 281 | // See math package in standard lib for details. 282 | // 283 | // If 'out' is nil a new array is created. 284 | // Will panic if 'out' and 'in' shapes don't match. 285 | func J0(out, in *NArray) *NArray { 286 | if out == nil { 287 | out = New(in.Shape...) 288 | } else if !EqualShape(out, in) { 289 | panic("J0:narrays must have equal shape.") 290 | } 291 | for k, v := range in.Data { 292 | out.Data[k] = float32(math.J0(float64(v))) 293 | } 294 | return out 295 | } 296 | 297 | // Y0 applies math.Y0() elementwise to a multidimensional array. 298 | // See math package in standard lib for details. 299 | // 300 | // If 'out' is nil a new array is created. 301 | // Will panic if 'out' and 'in' shapes don't match. 302 | func Y0(out, in *NArray) *NArray { 303 | if out == nil { 304 | out = New(in.Shape...) 305 | } else if !EqualShape(out, in) { 306 | panic("Y0:narrays must have equal shape.") 307 | } 308 | for k, v := range in.Data { 309 | out.Data[k] = float32(math.Y0(float64(v))) 310 | } 311 | return out 312 | } 313 | 314 | // J1 applies math.J1() elementwise to a multidimensional array. 315 | // See math package in standard lib for details. 316 | // 317 | // If 'out' is nil a new array is created. 318 | // Will panic if 'out' and 'in' shapes don't match. 319 | func J1(out, in *NArray) *NArray { 320 | if out == nil { 321 | out = New(in.Shape...) 322 | } else if !EqualShape(out, in) { 323 | panic("J1:narrays must have equal shape.") 324 | } 325 | for k, v := range in.Data { 326 | out.Data[k] = float32(math.J1(float64(v))) 327 | } 328 | return out 329 | } 330 | 331 | // Y1 applies math.Y1() elementwise to a multidimensional array. 332 | // See math package in standard lib for details. 333 | // 334 | // If 'out' is nil a new array is created. 335 | // Will panic if 'out' and 'in' shapes don't match. 336 | func Y1(out, in *NArray) *NArray { 337 | if out == nil { 338 | out = New(in.Shape...) 339 | } else if !EqualShape(out, in) { 340 | panic("Y1:narrays must have equal shape.") 341 | } 342 | for k, v := range in.Data { 343 | out.Data[k] = float32(math.Y1(float64(v))) 344 | } 345 | return out 346 | } 347 | 348 | // Log applies math.Log() elementwise to a multidimensional array. 349 | // See math package in standard lib for details. 350 | // 351 | // If 'out' is nil a new array is created. 352 | // Will panic if 'out' and 'in' shapes don't match. 353 | func Log(out, in *NArray) *NArray { 354 | if out == nil { 355 | out = New(in.Shape...) 356 | } else if !EqualShape(out, in) { 357 | panic("Log:narrays must have equal shape.") 358 | } 359 | for k, v := range in.Data { 360 | out.Data[k] = float32(math.Log(float64(v))) 361 | } 362 | return out 363 | } 364 | 365 | // Log10 applies math.Log10() elementwise to a multidimensional array. 366 | // See math package in standard lib for details. 367 | // 368 | // If 'out' is nil a new array is created. 369 | // Will panic if 'out' and 'in' shapes don't match. 370 | func Log10(out, in *NArray) *NArray { 371 | if out == nil { 372 | out = New(in.Shape...) 373 | } else if !EqualShape(out, in) { 374 | panic("Log10:narrays must have equal shape.") 375 | } 376 | for k, v := range in.Data { 377 | out.Data[k] = float32(math.Log10(float64(v))) 378 | } 379 | return out 380 | } 381 | 382 | // Log2 applies math.Log2() elementwise to a multidimensional array. 383 | // See math package in standard lib for details. 384 | // 385 | // If 'out' is nil a new array is created. 386 | // Will panic if 'out' and 'in' shapes don't match. 387 | func Log2(out, in *NArray) *NArray { 388 | if out == nil { 389 | out = New(in.Shape...) 390 | } else if !EqualShape(out, in) { 391 | panic("Log2:narrays must have equal shape.") 392 | } 393 | for k, v := range in.Data { 394 | out.Data[k] = float32(math.Log2(float64(v))) 395 | } 396 | return out 397 | } 398 | 399 | // Log1p applies math.Log1p() elementwise to a multidimensional array. 400 | // See math package in standard lib for details. 401 | // 402 | // If 'out' is nil a new array is created. 403 | // Will panic if 'out' and 'in' shapes don't match. 404 | func Log1p(out, in *NArray) *NArray { 405 | if out == nil { 406 | out = New(in.Shape...) 407 | } else if !EqualShape(out, in) { 408 | panic("Log1p:narrays must have equal shape.") 409 | } 410 | for k, v := range in.Data { 411 | out.Data[k] = float32(math.Log1p(float64(v))) 412 | } 413 | return out 414 | } 415 | 416 | // Logb applies math.Logb() elementwise to a multidimensional array. 417 | // See math package in standard lib for details. 418 | // 419 | // If 'out' is nil a new array is created. 420 | // Will panic if 'out' and 'in' shapes don't match. 421 | func Logb(out, in *NArray) *NArray { 422 | if out == nil { 423 | out = New(in.Shape...) 424 | } else if !EqualShape(out, in) { 425 | panic("Logb:narrays must have equal shape.") 426 | } 427 | for k, v := range in.Data { 428 | out.Data[k] = float32(math.Logb(float64(v))) 429 | } 430 | return out 431 | } 432 | 433 | // Cos applies math.Cos() elementwise to a multidimensional array. 434 | // See math package in standard lib for details. 435 | // 436 | // If 'out' is nil a new array is created. 437 | // Will panic if 'out' and 'in' shapes don't match. 438 | func Cos(out, in *NArray) *NArray { 439 | if out == nil { 440 | out = New(in.Shape...) 441 | } else if !EqualShape(out, in) { 442 | panic("Cos:narrays must have equal shape.") 443 | } 444 | for k, v := range in.Data { 445 | out.Data[k] = float32(math.Cos(float64(v))) 446 | } 447 | return out 448 | } 449 | 450 | // Sin applies math.Sin() elementwise to a multidimensional array. 451 | // See math package in standard lib for details. 452 | // 453 | // If 'out' is nil a new array is created. 454 | // Will panic if 'out' and 'in' shapes don't match. 455 | func Sin(out, in *NArray) *NArray { 456 | if out == nil { 457 | out = New(in.Shape...) 458 | } else if !EqualShape(out, in) { 459 | panic("Sin:narrays must have equal shape.") 460 | } 461 | for k, v := range in.Data { 462 | out.Data[k] = float32(math.Sin(float64(v))) 463 | } 464 | return out 465 | } 466 | 467 | // Sinh applies math.Sinh() elementwise to a multidimensional array. 468 | // See math package in standard lib for details. 469 | // 470 | // If 'out' is nil a new array is created. 471 | // Will panic if 'out' and 'in' shapes don't match. 472 | func Sinh(out, in *NArray) *NArray { 473 | if out == nil { 474 | out = New(in.Shape...) 475 | } else if !EqualShape(out, in) { 476 | panic("Sinh:narrays must have equal shape.") 477 | } 478 | for k, v := range in.Data { 479 | out.Data[k] = float32(math.Sinh(float64(v))) 480 | } 481 | return out 482 | } 483 | 484 | // Cosh applies math.Cosh() elementwise to a multidimensional array. 485 | // See math package in standard lib for details. 486 | // 487 | // If 'out' is nil a new array is created. 488 | // Will panic if 'out' and 'in' shapes don't match. 489 | func Cosh(out, in *NArray) *NArray { 490 | if out == nil { 491 | out = New(in.Shape...) 492 | } else if !EqualShape(out, in) { 493 | panic("Cosh:narrays must have equal shape.") 494 | } 495 | for k, v := range in.Data { 496 | out.Data[k] = float32(math.Cosh(float64(v))) 497 | } 498 | return out 499 | } 500 | 501 | // Tan applies math.Tan() elementwise to a multidimensional array. 502 | // See math package in standard lib for details. 503 | // 504 | // If 'out' is nil a new array is created. 505 | // Will panic if 'out' and 'in' shapes don't match. 506 | func Tan(out, in *NArray) *NArray { 507 | if out == nil { 508 | out = New(in.Shape...) 509 | } else if !EqualShape(out, in) { 510 | panic("Tan:narrays must have equal shape.") 511 | } 512 | for k, v := range in.Data { 513 | out.Data[k] = float32(math.Tan(float64(v))) 514 | } 515 | return out 516 | } 517 | 518 | // Tanh applies math.Tanh() elementwise to a multidimensional array. 519 | // See math package in standard lib for details. 520 | // 521 | // If 'out' is nil a new array is created. 522 | // Will panic if 'out' and 'in' shapes don't match. 523 | func Tanh(out, in *NArray) *NArray { 524 | if out == nil { 525 | out = New(in.Shape...) 526 | } else if !EqualShape(out, in) { 527 | panic("Tanh:narrays must have equal shape.") 528 | } 529 | for k, v := range in.Data { 530 | out.Data[k] = float32(math.Tanh(float64(v))) 531 | } 532 | return out 533 | } 534 | 535 | // Atan2 applies math.Atan2() elementwise to two multidimensional arrays. 536 | // See math package in standard lib for details. 537 | // 538 | // If out is nil a new array is created. 539 | // Will panic if 'out', 'a' and 'b' shapes don't match. 540 | func Atan2(out, a, b *NArray) *NArray { 541 | if out == nil { 542 | out = New(a.Shape...) 543 | } 544 | if !EqualShape(out, a, b) { 545 | panic("Atan2:narrays must have equal shape.") 546 | } 547 | for k, v := range a.Data { 548 | out.Data[k] = float32(math.Atan2(float64(v), float64(b.Data[k]))) 549 | } 550 | return out 551 | } 552 | 553 | // Dim applies math.Dim() elementwise to two multidimensional arrays. 554 | // See math package in standard lib for details. 555 | // 556 | // If out is nil a new array is created. 557 | // Will panic if 'out', 'a' and 'b' shapes don't match. 558 | func Dim(out, a, b *NArray) *NArray { 559 | if out == nil { 560 | out = New(a.Shape...) 561 | } 562 | if !EqualShape(out, a, b) { 563 | panic("Dim:narrays must have equal shape.") 564 | } 565 | for k, v := range a.Data { 566 | out.Data[k] = float32(math.Dim(float64(v), float64(b.Data[k]))) 567 | } 568 | return out 569 | } 570 | 571 | // Hypot applies math.Hypot() elementwise to two multidimensional arrays. 572 | // See math package in standard lib for details. 573 | // 574 | // If out is nil a new array is created. 575 | // Will panic if 'out', 'a' and 'b' shapes don't match. 576 | func Hypot(out, a, b *NArray) *NArray { 577 | if out == nil { 578 | out = New(a.Shape...) 579 | } 580 | if !EqualShape(out, a, b) { 581 | panic("Hypot:narrays must have equal shape.") 582 | } 583 | for k, v := range a.Data { 584 | out.Data[k] = float32(math.Hypot(float64(v), float64(b.Data[k]))) 585 | } 586 | return out 587 | } 588 | 589 | // Mod applies math.Mod() elementwise to two multidimensional arrays. 590 | // See math package in standard lib for details. 591 | // 592 | // If out is nil a new array is created. 593 | // Will panic if 'out', 'a' and 'b' shapes don't match. 594 | func Mod(out, a, b *NArray) *NArray { 595 | if out == nil { 596 | out = New(a.Shape...) 597 | } 598 | if !EqualShape(out, a, b) { 599 | panic("Mod:narrays must have equal shape.") 600 | } 601 | for k, v := range a.Data { 602 | out.Data[k] = float32(math.Mod(float64(v), float64(b.Data[k]))) 603 | } 604 | return out 605 | } 606 | 607 | // Pow applies math.Pow() elementwise to two multidimensional arrays. 608 | // See math package in standard lib for details. 609 | // 610 | // If out is nil a new array is created. 611 | // Will panic if 'out', 'a' and 'b' shapes don't match. 612 | func Pow(out, a, b *NArray) *NArray { 613 | if out == nil { 614 | out = New(a.Shape...) 615 | } 616 | if !EqualShape(out, a, b) { 617 | panic("Pow:narrays must have equal shape.") 618 | } 619 | for k, v := range a.Data { 620 | out.Data[k] = float32(math.Pow(float64(v), float64(b.Data[k]))) 621 | } 622 | return out 623 | } 624 | 625 | // Remainder applies math.Remainder() elementwise to two multidimensional arrays. 626 | // See math package in standard lib for details. 627 | // 628 | // If out is nil a new array is created. 629 | // Will panic if 'out', 'a' and 'b' shapes don't match. 630 | func Remainder(out, a, b *NArray) *NArray { 631 | if out == nil { 632 | out = New(a.Shape...) 633 | } 634 | if !EqualShape(out, a, b) { 635 | panic("Remainder:narrays must have equal shape.") 636 | } 637 | for k, v := range a.Data { 638 | out.Data[k] = float32(math.Remainder(float64(v), float64(b.Data[k]))) 639 | } 640 | return out 641 | } 642 | -------------------------------------------------------------------------------- /na32/narray.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | /* 9 | Package narray provides functions to opearate with multidimensional arrays of type float32. The NArray object is a dense, fixed-size, array of rank n. 10 | 11 | The shape is a vector with the size of each dimension. Typical cases: 12 | 13 | type rank example 14 | -------------------------------- 15 | scalar 0 na := New() 16 | vector 1 na := New(12) 17 | matrix 2 na := New(5,17) 18 | cube 3 na := New(2,3,5) 19 | 20 | */ 21 | package na32 22 | 23 | import ( 24 | "bytes" 25 | "encoding/json" 26 | "fmt" 27 | "io" 28 | "math" 29 | "math/rand" 30 | "os" 31 | "path/filepath" 32 | "strconv" 33 | ) 34 | 35 | // The NArray object. 36 | type NArray struct { 37 | // The rank or order or degree of the narray is the dimensionality required to represent it. (eg. The rank of a vector is 1) 38 | Rank int `json:"rank"` 39 | // The shape is an int slice that contains the size of each dimension. Subscripts range from zero to s-1. Where s is the size of a dimension. 40 | Shape []int `json:"shape"` 41 | // The data is stored as a slice of float32 numbers. 42 | Data []float32 `json:"data"` 43 | // Strides for each dimension. 44 | Strides []int `json:"strides"` 45 | } 46 | 47 | // New creates a new n-dimensional array. 48 | func New(shape ...int) *NArray { 49 | 50 | size := 1 51 | rank := len(shape) 52 | for _, v := range shape { 53 | size *= v 54 | } 55 | strides := make([]int, rank, rank) 56 | s := 1 57 | for i := (rank - 1); i >= 0; i-- { 58 | strides[i] = s 59 | s *= shape[i] 60 | } 61 | 62 | return &NArray{ 63 | Rank: rank, 64 | Shape: shape, 65 | Data: make([]float32, size, size), 66 | Strides: strides, 67 | } 68 | } 69 | 70 | // NewArray creates a new n-dimensional array with content of a slice 71 | // The size of the slice must match the product of the slice, 72 | // otherwise a panic will occur 73 | func NewArray(a []float32, shape ...int) *NArray { 74 | size := 1 75 | rank := len(shape) 76 | for _, v := range shape { 77 | size *= v 78 | } 79 | strides := make([]int, rank, rank) 80 | s := 1 81 | for i := (rank - 1); i >= 0; i-- { 82 | strides[i] = s 83 | s *= shape[i] 84 | } 85 | 86 | if len(a) != size { 87 | panic("slice doesn't match size") 88 | } 89 | return &NArray{ 90 | Rank: rank, 91 | Shape: shape, 92 | Data: a, 93 | Strides: strides, 94 | } 95 | } 96 | 97 | // Norm creates a new n-dimensional array whose 98 | // elements are drawn from a Normal probability density function. 99 | func Norm(r *rand.Rand, mean, sd float32, shape ...int) *NArray { 100 | 101 | na := New(shape...) 102 | for i := range na.Data { 103 | na.Data[i] = float32(r.NormFloat64())*sd + mean 104 | } 105 | return na 106 | } 107 | 108 | // Rand creates a new n-dimensional array whose 109 | // elements are set using the rand.Float64 function. 110 | // Values are pseudo-random numbers in [0.0,1.0). 111 | func Rand(r *rand.Rand, shape ...int) *NArray { 112 | 113 | na := New(shape...) 114 | for i := range na.Data { 115 | na.Data[i] = r.Float32() 116 | } 117 | return na 118 | } 119 | 120 | // At returns the value for indices. 121 | func (na *NArray) At(indices ...int) float32 { 122 | 123 | if len(indices) != na.Rank { 124 | fmt.Errorf("inconsistent number of indices for narray - [%d] vs [%d]", len(indices), na.Rank) 125 | } 126 | 127 | // return na.Data[na.Index(indices...)] 128 | return na.Data[na.Index(indices...)] 129 | } 130 | 131 | // Set value for indices. 132 | func (na *NArray) Set(v float32, indices ...int) { 133 | 134 | na.Data[na.Index(indices...)] = v 135 | } 136 | 137 | // Index transforms a set of subscripts to a an index in the underlying one-dimensional slice. 138 | func (na *NArray) Index(indices ...int) int { 139 | 140 | idx := 0 141 | for k, v := range indices { 142 | idx += v * na.Strides[k] 143 | } 144 | return idx 145 | } 146 | 147 | // ReverseIndex converts a linear index to narray indices. 148 | func (na *NArray) ReverseIndex(idx int) []int { 149 | 150 | res := make([]int, na.Rank, na.Rank) 151 | temp := idx 152 | p := 1 153 | for k := 1; k < na.Rank; k++ { 154 | p *= na.Shape[k] 155 | } 156 | for i := 0; i < na.Rank; i++ { 157 | res[i] = temp / p 158 | temp = temp % p 159 | if (i + 1) < na.Rank { 160 | p /= na.Shape[i+1] 161 | } 162 | } 163 | return res 164 | } 165 | 166 | // Copy returns a copy on the narray. 167 | func (na *NArray) Copy() *NArray { 168 | 169 | newna := New(na.Shape...) 170 | copy(newna.Data, na.Data) 171 | return newna 172 | } 173 | 174 | // ApplyFunc is a type for creating custom functions. 175 | type ApplyFunc func(x float32) float32 176 | 177 | // Apply function of type ApplyFunc to a multidimensional array. 178 | // If out is nil, a new object is allocated. 179 | func Apply(out, in *NArray, fn ApplyFunc) *NArray { 180 | 181 | if out == nil { 182 | out = New(in.Shape...) 183 | } 184 | for i := 0; i < len(in.Data); i++ { 185 | out.Data[i] = fn(in.Data[i]) 186 | } 187 | return out 188 | } 189 | 190 | // EqualShape returns true if all the arrays have equal length, 191 | // and false otherwise. Returns true if there is only one input array. 192 | func EqualShape(x *NArray, ys ...*NArray) bool { 193 | shape := x.Shape 194 | l := len(shape) 195 | for _, y := range ys { 196 | if len(y.Shape) != l { 197 | return false 198 | } 199 | for j, d := range shape { 200 | if y.Shape[j] != d { 201 | return false 202 | } 203 | } 204 | } 205 | return true 206 | } 207 | 208 | // Inc increments the value of an narray element. 209 | func (na *NArray) Inc(v float32, indices ...int) { 210 | 211 | na.Data[na.Index(indices...)] += v 212 | } 213 | 214 | // MaxElem compares value to element and replaces element if 215 | // value is greater than element. 216 | func (na *NArray) MaxElem(v float32, indices ...int) { 217 | 218 | idx := na.Index(indices...) 219 | if v > na.Data[idx] { 220 | na.Data[idx] = v 221 | } 222 | } 223 | 224 | // MinElem compares value to element and replaces element if 225 | // value is less than element. 226 | func (na *NArray) MinElem(v float32, indices ...int) { 227 | 228 | idx := na.Index(indices...) 229 | if v < na.Data[idx] { 230 | na.Data[idx] = v 231 | } 232 | } 233 | 234 | // Add adds narrays elementwise. 235 | // out = sum_i(in[i]) 236 | // Will panic if there are not at least two input narrays 237 | // or if narray shapes don't match. 238 | // If out is nil a new array is created. 239 | func Add(out *NArray, in ...*NArray) *NArray { 240 | 241 | if len(in) < 2 { 242 | return nil 243 | } 244 | if out == nil { 245 | out = New(in[0].Shape...) 246 | } 247 | if !EqualShape(out, in...) { 248 | panic("narrays must have equal shape.") 249 | } 250 | 251 | addSlice(out.Data, in[0].Data, in[1].Data) 252 | 253 | // Multiply each following, if more than two arguments. 254 | for k := 2; k < len(in); k++ { 255 | addSlice(out.Data, out.Data, in[k].Data) 256 | } 257 | 258 | return out 259 | } 260 | 261 | // Mul multiplies narrays elementwise. 262 | // out = prod_i(in[i]) 263 | // Will panic if there are not at least two input narrays 264 | // or if narray shapes don't match. 265 | // If out is nil a new array is created. 266 | func Mul(out *NArray, in ...*NArray) *NArray { 267 | 268 | if len(in) < 2 { 269 | panic("not in enough arguments") 270 | } 271 | if out == nil { 272 | out = New(in[0].Shape...) 273 | } 274 | if !EqualShape(out, in...) { 275 | panic("narrays must have equal shape.") 276 | } 277 | 278 | mulSlice(out.Data, in[0].Data, in[1].Data) 279 | 280 | // Multiply each following, if more than two arguments. 281 | for k := 2; k < len(in); k++ { 282 | mulSlice(out.Data, out.Data, in[k].Data) 283 | } 284 | return out 285 | } 286 | 287 | // Dot computes the sum of the elementwise products of 288 | // the input arrays. 289 | // 290 | // y = sum_{i = 0}^(N-1) x0[i]*x1[i]*...x_n-1[i] 291 | // 292 | // Will panic if there are not at least two input narrays 293 | // or if narray shapes don't match. 294 | func Dot(in ...*NArray) float32 { 295 | 296 | if len(in) < 2 { 297 | panic("not in enough arguments") 298 | } 299 | if !EqualShape(in[0], in...) { 300 | panic("narrays must have equal shape.") 301 | } 302 | n := len(in[0].Data) 303 | out := make([]float32, n, n) 304 | mulSlice(out, in[0].Data, in[1].Data) 305 | 306 | // Multiply each following, if more than two arguments. 307 | for k := 2; k < len(in); k++ { 308 | mulSlice(out, out, in[k].Data) 309 | } 310 | return sliceSum(out) 311 | } 312 | 313 | // Div divides narrays elementwise. 314 | // out = in[0] / in[1] / in[2] .... 315 | // Will panic if there are not at least two input narrays 316 | // or if narray shapes don't match. 317 | // If out is nil a new array is created. 318 | func Div(out *NArray, in ...*NArray) *NArray { 319 | 320 | if len(in) < 2 { 321 | panic("not in enough arguments") 322 | } 323 | if out == nil { 324 | out = New(in[0].Shape...) 325 | } 326 | if !EqualShape(out, in...) { 327 | panic("narrays must have equal shape.") 328 | } 329 | 330 | divSlice(out.Data, in[0].Data, in[1].Data) 331 | 332 | // Multiply each following, if more than two arguments. 333 | for k := 2; k < len(in); k++ { 334 | divSlice(out.Data, out.Data, in[k].Data) 335 | } 336 | return out 337 | } 338 | 339 | // Sub subtracts narrays elementwise. 340 | // out = in[0] - in[1] - in[2] .... 341 | // Will panic if there are not at least two input narrays 342 | // or if narray shapes don't match. 343 | // If out is nil a new array is created. 344 | func Sub(out *NArray, in ...*NArray) *NArray { 345 | 346 | if len(in) < 2 { 347 | panic("not in enough arguments") 348 | } 349 | if out == nil { 350 | out = New(in[0].Shape...) 351 | } 352 | if !EqualShape(out, in...) { 353 | panic("narrays must have equal shape.") 354 | } 355 | 356 | subSlice(out.Data, in[0].Data, in[1].Data) 357 | 358 | // Multiply each following, if more than two arguments. 359 | for k := 2; k < len(in); k++ { 360 | subSlice(out.Data, out.Data, in[k].Data) 361 | } 362 | return out 363 | } 364 | 365 | // AddConst adds const to an narray elementwise. 366 | // out = in + c 367 | // If out is nil a new array is created. 368 | func AddConst(out *NArray, in *NArray, c float32) *NArray { 369 | 370 | if out == nil { 371 | out = New(in.Shape...) 372 | } else { 373 | if !EqualShape(out, in) { 374 | panic("narrays must have equal shape.") 375 | } 376 | } 377 | caddSlice(out.Data, in.Data, c) 378 | return out 379 | } 380 | 381 | // AddScaled adds a scaled narray elementwise. 382 | // y = y + a * x 383 | // If y is nil a new array is created. 384 | func AddScaled(y *NArray, x *NArray, a float32) *NArray { 385 | 386 | if y == nil { 387 | y = New(x.Shape...) 388 | } else { 389 | if !EqualShape(y, x) { 390 | panic("narrays must have equal shape.") 391 | } 392 | } 393 | addScaledSlice(y.Data, x.Data, a) 394 | return y 395 | } 396 | 397 | // Scale multiplies an narray by a factor elementwise. 398 | // out = c * in 399 | // If out is nil a new array is created. 400 | func Scale(out *NArray, in *NArray, c float32) *NArray { 401 | 402 | if out == nil { 403 | out = New(in.Shape...) 404 | } else { 405 | if !EqualShape(out, in) { 406 | panic("narrays must have equal shape.") 407 | } 408 | } 409 | cmulSlice(out.Data, in.Data, c) 410 | return out 411 | } 412 | 413 | // Rcp returns reciprocal values of narrays elementwise. 414 | // out = 1.0 / in 415 | // If out is nil a new array is created. 416 | func Rcp(out, in *NArray) *NArray { 417 | if out == nil { 418 | out = New(in.Shape...) 419 | } else { 420 | if !EqualShape(out, in) { 421 | panic("narrays must have equal shape.") 422 | } 423 | } 424 | cdivSlice(out.Data, in.Data, 1.0) 425 | return out 426 | } 427 | 428 | // Sqrt returns square root values of narrays elementwise. 429 | // out = math.Sqrt(in) 430 | // If out is nil a new array is created. 431 | func Sqrt(out, in *NArray) *NArray { 432 | if out == nil { 433 | out = New(in.Shape...) 434 | } else { 435 | if !EqualShape(out, in) { 436 | panic("narrays must have equal shape.") 437 | } 438 | } 439 | sqrtSlice(out.Data, in.Data) 440 | return out 441 | } 442 | 443 | // Abs returns square root values of narrays elementwise. 444 | // out = math.Abs(in) 445 | // If out is nil a new array is created. 446 | func Abs(out, in *NArray) *NArray { 447 | if out == nil { 448 | out = New(in.Shape...) 449 | } else { 450 | if !EqualShape(out, in) { 451 | panic("narrays must have equal shape.") 452 | } 453 | } 454 | absSlice(out.Data, in.Data) 455 | return out 456 | } 457 | 458 | // Max returns the max value in the narray. 459 | func (na *NArray) Max() float32 { 460 | if na == nil || len(na.Data) == 0 { 461 | panic("unable to take max of nil or zero-sizes array") 462 | } 463 | return maxSliceElement(na.Data) 464 | } 465 | 466 | // MaxIdx returns the max value and corresponding indices. 467 | func (na *NArray) MaxIdx() (float32, []int) { 468 | 469 | var offset int 470 | max := float32(-math.MaxFloat32) 471 | for i := 0; i < len(na.Data); i++ { 472 | if na.Data[i] > max { 473 | max = na.Data[i] 474 | offset = i 475 | } 476 | } 477 | return max, na.ReverseIndex(offset) 478 | } 479 | 480 | // MaxArray compare input narrays and returns an narray containing 481 | // the element-wise maxima. 482 | // out[i,j,k,...] = max(in0[i,j,k,...], in1[i,j,k,...], ...) 483 | // Will panic if there are not at least two input narray 484 | // or if narray shapes don't match. 485 | // If out is nil a new array is created. 486 | func MaxArray(out *NArray, in ...*NArray) *NArray { 487 | 488 | if len(in) < 2 { 489 | panic("not in enough input narrays") 490 | } 491 | if out == nil { 492 | out = New(in[0].Shape...) 493 | } 494 | if !EqualShape(out, in...) { 495 | panic("narrays must have equal shape.") 496 | } 497 | 498 | maxSlice(out.Data, in[0].Data, in[1].Data) 499 | 500 | // Also add each following, if more than two arguments. 501 | for k := 2; k < len(in); k++ { 502 | maxSlice(out.Data, out.Data, in[k].Data) 503 | } 504 | return out 505 | } 506 | 507 | // Copysign returns values with the magnitude of a and the sign of b 508 | // for each element of the arrays. 509 | // Will panic if narray shapes don't match. 510 | // If out is nil a new array is created. 511 | func Copysign(out, a, b *NArray) *NArray { 512 | 513 | if out == nil { 514 | out = New(a.Shape...) 515 | } 516 | if !EqualShape(out, a, b) { 517 | panic("narrays must have equal shape.") 518 | } 519 | 520 | csignSlice(out.Data, a.Data, b.Data) 521 | 522 | return out 523 | } 524 | 525 | // Min returns the min value in the narray. 526 | func (na *NArray) Min() float32 { 527 | if na == nil || len(na.Data) == 0 { 528 | panic("unable to take min of nil or zero-sizes array") 529 | } 530 | return minSliceElement(na.Data) 531 | } 532 | 533 | // MinIdx returns the min value and corresponding indices. 534 | func (na *NArray) MinIdx() (float32, []int) { 535 | 536 | var offset int 537 | min := float32(math.MaxFloat32) 538 | for i := 0; i < len(na.Data); i++ { 539 | if na.Data[i] < min { 540 | min = na.Data[i] 541 | offset = i 542 | } 543 | } 544 | return min, na.ReverseIndex(offset) 545 | } 546 | 547 | // MinArray compare input narrays and returns an narray containing 548 | // the element-wise minima. 549 | // out[i,j,k,...] = min(in0[i,j,k,...], in1[i,j,k,...], ...) 550 | // Will panic if there are not at least two input narray 551 | // or if narray shapes don't match. 552 | // If out is nil a new array is created. 553 | func MinArray(out *NArray, in ...*NArray) *NArray { 554 | 555 | if len(in) < 2 { 556 | panic("not in enough input narrays") 557 | } 558 | if out == nil { 559 | out = New(in[0].Shape...) 560 | } 561 | if !EqualShape(out, in...) { 562 | panic("narrays must have equal shape.") 563 | } 564 | 565 | minSlice(out.Data, in[0].Data, in[1].Data) 566 | 567 | // Also add each following, if more than two arguments. 568 | for k := 2; k < len(in); k++ { 569 | minSlice(out.Data, out.Data, in[k].Data) 570 | } 571 | 572 | return out 573 | } 574 | 575 | // Prod returns the products of all the elements in the narray. 576 | func (na *NArray) Prod() float32 { 577 | 578 | p := float32(1.0) 579 | for _, v := range na.Data { 580 | p *= v 581 | } 582 | return p 583 | } 584 | 585 | // Sum returns the sum of all the elements in the narray. 586 | func (na *NArray) Sum() float32 { 587 | return sliceSum(na.Data) 588 | } 589 | 590 | // SetValue sets all elements to value. 591 | func (na *NArray) SetValue(v float32) *NArray { 592 | 593 | for i := range na.Data { 594 | na.Data[i] = v 595 | } 596 | return na 597 | } 598 | 599 | // Encode converts values in-place as follows: 600 | // Inf to math.MaxFloat64 601 | // -Inf to -math.MaxFloat64 602 | // NaN ro 0 603 | // 604 | // Returns the indices of the modified values as follows: 605 | // Values in inf => na.Data[abs(v)] = sign(v) * Inf 606 | // Values in nan => na.Data[v] = NaN 607 | func (na *NArray) Encode() (inf, nan []int) { 608 | 609 | inf = []int{} 610 | nan = []int{} 611 | for k, v := range na.Data { 612 | switch { 613 | case math.IsInf(float64(v), 1): 614 | na.Data[k] = float32(math.MaxFloat32) 615 | inf = append(inf, k) 616 | case math.IsInf(float64(v), -1): 617 | na.Data[k] = float32(-math.MaxFloat32) 618 | inf = append(inf, -k) 619 | case math.IsNaN(float64(v)): 620 | na.Data[k] = 0 621 | nan = append(nan, k) 622 | } 623 | } 624 | return 625 | } 626 | 627 | // Decode converts values in-place. 628 | // See Encode() for details. 629 | func (na *NArray) Decode(inf, nan []int) { 630 | pInf := float32(math.Inf(1)) 631 | nInf := float32(math.Inf(-1)) 632 | fNan := float32(math.NaN()) 633 | 634 | for _, v := range inf { 635 | if v >= 0 { 636 | na.Data[v] = pInf 637 | } else { 638 | na.Data[-v] = nInf 639 | } 640 | } 641 | for _, v := range nan { 642 | na.Data[v] = fNan 643 | } 644 | } 645 | 646 | // SubArray returns an narray of lower rank as follows: 647 | // 648 | // Example, given an narray with shape 2x3x4 (rank=3), return the subarray 649 | // of rank=2 corresponding to dim[2]=1 650 | // 651 | // x := New(2,3,4) 652 | // y := x.SubArray(-1,-1,1) // use -1 to select a dimension. Put a 1 in dim=2 (third argument). 653 | // // y = {x(0,0,1), x(0,1,1), x(0,2,1), x(1,0,1), ...} 654 | // 655 | func (na *NArray) SubArray(query ...int) *NArray { 656 | 657 | if len(na.Shape) == 0 { 658 | panic("cannot get subarray from narray with rank=0") 659 | } 660 | 661 | qs := querySubset(query, na.Shape) 662 | var ns []int // new shape 663 | for k, v := range query { 664 | if v < 0 { 665 | ns = append(ns, na.Shape[k]) 666 | } 667 | } 668 | newArr := New(ns...) 669 | for k, v := range qs { 670 | newArr.Data[k] = na.At(v...) 671 | } 672 | 673 | return newArr 674 | } 675 | 676 | // Reshape returns an narray with a new shape. 677 | func (na *NArray) Reshape(dim ...int) *NArray { 678 | panic("not implemented") 679 | } 680 | 681 | // Sprint prints narray elements when f returns true. 682 | // index is the linear index of an narray. 683 | func (na *NArray) Sprint(f func(na *NArray, index int) bool) string { 684 | 685 | b := bytes.NewBufferString(fmt.Sprintln("narray rank: ", na.Rank)) 686 | _, _ = b.WriteString(fmt.Sprintln("narray shape: ", na.Shape)) 687 | for k, v := range na.Data { 688 | idx := na.ReverseIndex(k) 689 | if f(na, k) { 690 | _, _ = b.WriteString("[") 691 | for axis, av := range idx { 692 | _, _ = b.WriteString(formatted(av, na.Shape[axis]-1)) 693 | } 694 | _, _ = b.WriteString(fmt.Sprintf("] => %f\n", v)) 695 | } 696 | } 697 | return b.String() 698 | } 699 | 700 | // Read unmarshals json data from an io.Reader into an narray struct. 701 | func Read(r io.Reader) (*NArray, error) { 702 | dec := json.NewDecoder(r) 703 | var na NArray 704 | err := dec.Decode(&na) 705 | if err != nil && err != io.EOF { 706 | return nil, err 707 | } 708 | return &na, nil 709 | } 710 | 711 | // ReadFile unmarshals json data from a file into an narray struct. 712 | func ReadFile(fn string) (*NArray, error) { 713 | 714 | f, err := os.Open(fn) 715 | if err != nil { 716 | return nil, err 717 | } 718 | defer f.Close() 719 | return Read(f) 720 | } 721 | 722 | // Write writes narray to an io.Writer. 723 | func (na *NArray) Write(w io.Writer) error { 724 | 725 | enc := json.NewEncoder(w) 726 | err := enc.Encode(na) 727 | if err != nil { 728 | return err 729 | } 730 | return nil 731 | } 732 | 733 | // WriteFile writes an narray to a file. 734 | func (na *NArray) WriteFile(fn string) error { 735 | 736 | e := os.MkdirAll(filepath.Dir(fn), 0755) 737 | if e != nil { 738 | return e 739 | } 740 | f, err := os.Create(fn) 741 | if err != nil { 742 | return err 743 | } 744 | defer f.Close() 745 | 746 | ee := na.Write(f) 747 | if ee != nil { 748 | return ee 749 | } 750 | return nil 751 | } 752 | 753 | // ToJSON returns a json string. 754 | func (na *NArray) ToJSON() (string, error) { 755 | var b bytes.Buffer 756 | err := na.Write(&b) 757 | return b.String(), err 758 | } 759 | 760 | // MarshalJSON implements the json.Marshaller interface. 761 | // The custom marshaller is needed to encode Inf/NaN values. 762 | func (na *NArray) MarshalJSON() ([]byte, error) { 763 | 764 | ena := na.Copy() 765 | inf, nan := ena.Encode() 766 | return json.Marshal(struct { 767 | Rank int `json:"rank"` 768 | Shape []int `json:"shape"` 769 | Data []float32 `json:"data"` 770 | Strides []int `json:"strides"` 771 | Inf []int `json:"inf,omitempty"` 772 | NaN []int `json:"nan,omitempty"` 773 | }{ 774 | Rank: ena.Rank, 775 | Shape: ena.Shape, 776 | Data: ena.Data, 777 | Strides: ena.Strides, 778 | Inf: inf, 779 | NaN: nan, 780 | }) 781 | } 782 | 783 | // UnmarshalJSON implements the json.Unarshaller interface. 784 | // The custom unmarshaller is needed to decode Inf/NaN values. 785 | func (na *NArray) UnmarshalJSON(b []byte) error { 786 | x := struct { 787 | Rank int `json:"rank"` 788 | Shape []int `json:"shape"` 789 | Data []float32 `json:"data"` 790 | Strides []int `json:"strides"` 791 | Inf []int `json:"inf,omitempty"` 792 | NaN []int `json:"nan,omitempty"` 793 | }{} 794 | 795 | err := json.Unmarshal(b, &x) 796 | if err != nil { 797 | return err 798 | } 799 | 800 | na.Rank = x.Rank 801 | na.Shape = x.Shape 802 | na.Data = x.Data 803 | na.Strides = x.Strides 804 | na.Decode(x.Inf, x.NaN) 805 | return nil 806 | } 807 | 808 | // String prints the narray 809 | func (na *NArray) String() string { 810 | 811 | return na.Sprint(func(na *NArray, k int) bool { 812 | return true 813 | }) 814 | } 815 | 816 | // equal returns true if |x-y|/(|avg(x,y)|+1) < tol. 817 | func equal(x, y float32, tol float64) bool { 818 | avg := (math.Abs(float64(x+y)) / 2.0) 819 | sErr := math.Abs(float64(x-y)) / (avg + 1) 820 | if sErr > tol { 821 | return false 822 | } 823 | return true 824 | } 825 | 826 | // EqualValues compares two narrays elementwise. 827 | // Returns true if for all elements |x-y|/(|avg(x,y)|+1) < tol. 828 | func EqualValues(x *NArray, y *NArray, tol float64) bool { 829 | if !EqualShape(x, y) { 830 | panic("narrays must have equal shape.") 831 | } 832 | for i, _ := range x.Data { 833 | if !equal(x.Data[i], y.Data[i], tol) { 834 | return false 835 | } 836 | } 837 | return true 838 | } 839 | 840 | func formatted(n, max int) string { 841 | b := bytes.NewBufferString(" ") 842 | for i := 0; i < nd(max)-nd(n); i++ { 843 | _, _ = b.WriteString(" ") 844 | } 845 | _, _ = b.WriteString(strconv.FormatInt(int64(n), 10)) 846 | return b.String() 847 | } 848 | 849 | // num digits in number 850 | func nd(n int) int { 851 | if n == 0 { 852 | return 1 853 | } 854 | return int(math.Log10(float64(n))) + 1 855 | } 856 | 857 | func cartesianProduct(s []int) [][]int { 858 | 859 | if len(s) == 1 { 860 | z := make([][]int, s[0], s[0]) 861 | for k := range z { 862 | z[k] = []int{k} 863 | } 864 | return z 865 | } 866 | var result [][]int 867 | for i := 0; i < s[0]; i++ { 868 | x := cartesianProduct(s[1:]) 869 | for _, v := range x { 870 | var sl []int 871 | sl = append(sl, i) 872 | sl = append(sl, v...) 873 | result = append(result, sl) 874 | } 875 | } 876 | return result 877 | } 878 | 879 | // Recursively find indices for query q. 880 | // Helper func to generate narray subsets. 881 | func querySubset(q, s []int) [][]int { 882 | 883 | if len(q) != len(s) { 884 | panic("size mismatch") 885 | } 886 | var result [][]int 887 | 888 | switch { 889 | case len(s) == 1 && q[0] >= 0: 890 | result = [][]int{[]int{q[0]}} 891 | 892 | case len(s) == 1 && q[0] < 0: 893 | result = make([][]int, s[0], s[0]) 894 | for k := range result { 895 | result[k] = []int{k} 896 | } 897 | 898 | case q[0] >= 0: 899 | x := querySubset(q[1:], s[1:]) 900 | for _, v := range x { 901 | var sl []int 902 | sl = append(sl, q[0]) 903 | sl = append(sl, v...) 904 | result = append(result, sl) 905 | } 906 | 907 | case q[0] < 0: 908 | for i := 0; i < s[0]; i++ { 909 | x := querySubset(q[1:], s[1:]) 910 | for _, v := range x { 911 | var sl []int 912 | sl = append(sl, i) 913 | sl = append(sl, v...) 914 | result = append(result, sl) 915 | } 916 | } 917 | } 918 | return result 919 | } 920 | -------------------------------------------------------------------------------- /na64/arrayfuncs.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // +build !amd64 4 | 5 | package na64 6 | 7 | import ( 8 | "math" 9 | ) 10 | 11 | // These are the fallbacks that are used when not on AMD64 platform. 12 | 13 | // divSlice divides two slices 14 | // Assumptions the assembly can make: 15 | // out != nil, a != nil, b != nil 16 | // len(out) == len(a) == len(b) 17 | func divSlice(out, a, b []float64) { 18 | for i := 0; i < len(out); i++ { 19 | out[i] = a[i] / b[i] 20 | } 21 | } 22 | 23 | // addSlice adds two slices 24 | // Assumptions the assembly can make: 25 | // out != nil, a != nil, b != nil 26 | // len(out) == len(a) == len(b) 27 | func addSlice(out, a, b []float64) { 28 | for i := 0; i < len(out); i++ { 29 | out[i] = a[i] + b[i] 30 | } 31 | } 32 | 33 | // subSlice subtracts two slices 34 | // Assumptions the assembly can make: 35 | // out != nil, a != nil, b != nil 36 | // len(out) == len(a) == len(b) 37 | func subSlice(out, a, b []float64) { 38 | for i := 0; i < len(out); i++ { 39 | out[i] = a[i] - b[i] 40 | } 41 | } 42 | 43 | // mulSlice multiply two slices 44 | // Assumptions the assembly can make: 45 | // out != nil, a != nil, b != nil 46 | // len(out) == len(a) == len(b) 47 | func mulSlice(out, a, b []float64) { 48 | for i := 0; i < len(out); i++ { 49 | out[i] = a[i] * b[i] 50 | } 51 | } 52 | 53 | // minSlice returns lowest valus of two slices 54 | // Assumptions the assembly can make: 55 | // out != nil, a != nil, b != nil 56 | // len(out) == len(a) == len(b) 57 | func minSlice(out, a, b []float64) { 58 | for i := 0; i < len(out); i++ { 59 | if a[i] < b[i] { 60 | out[i] = a[i] 61 | } else { 62 | out[i] = b[i] 63 | } 64 | } 65 | } 66 | 67 | // maxSlice return maximum of two slices 68 | // Assumptions the assembly can make: 69 | // out != nil, a != nil, b != nil 70 | // len(out) == len(a) == len(b) 71 | func maxSlice(out, a, b []float64) { 72 | for i := 0; i < len(out); i++ { 73 | if a[i] > b[i] { 74 | out[i] = a[i] 75 | } else { 76 | out[i] = b[i] 77 | } 78 | } 79 | } 80 | 81 | // csignSlice returns a value with the magnitude of a and the sign of b 82 | // for each element in the slice. 83 | // Assumptions the assembly can make: 84 | // out != nil, a != nil, b != nil 85 | // len(out) == len(a) == len(b) 86 | func csignSlice(out, a, b []float64) { 87 | const sign = 1 << 63 88 | for i := 0; i < len(out); i++ { 89 | out[i] = math.Float64frombits(math.Float64bits(a[i])&^sign | math.Float64bits(b[i])&sign) 90 | 91 | } 92 | } 93 | 94 | // cdivSlice will return c / values of the array 95 | // Assumptions the assembly can make: 96 | // out != nil, a != nil 97 | // len(out) == len(a) 98 | func cdivSlice(out, a []float64, c float64) { 99 | for i := 0; i < len(out); i++ { 100 | out[i] = c / a[i] 101 | } 102 | } 103 | 104 | // cmulSlice will return c * values of the array 105 | // Assumptions the assembly can make: 106 | // out != nil, a != nil 107 | // len(out) == len(a) 108 | func cmulSlice(out, a []float64, c float64) { 109 | for i := 0; i < len(out); i++ { 110 | out[i] = c * a[i] 111 | } 112 | } 113 | 114 | // caddSlice will return c * values of the array 115 | // Assumptions the assembly can make: 116 | // out != nil, a != nil 117 | // len(out) == len(a) 118 | func caddSlice(out, a []float64, c float64) { 119 | for i := 0; i < len(out); i++ { 120 | out[i] = c + a[i] 121 | } 122 | } 123 | 124 | // addScaledSlice adds a scaled narray elementwise. 125 | // y = y + a * x 126 | // Assumptions the assembly can make: 127 | // y != nil, a != nil 128 | // len(x) == len(y) 129 | func addScaledSlice(y, x []float64, a float64) { 130 | for i, v := range x { 131 | y[i] += v * a 132 | } 133 | } 134 | 135 | // sqrtSlice will return math.Sqrt(values) of the array 136 | // Assumptions the assembly can make: 137 | // out != nil, a != nil 138 | // len(out) == len(a) 139 | func sqrtSlice(out, a []float64) { 140 | for i := 0; i < len(out); i++ { 141 | out[i] = float64(math.Sqrt(float64(a[i]))) 142 | } 143 | } 144 | 145 | // minSliceElement will the smallest value of the slice 146 | // Assumptions the assembly can make: 147 | // a != nil 148 | // len(a) > 0 149 | func minSliceElement(a []float64) float64 { 150 | min := a[0] 151 | for i := 1; i < len(a); i++ { 152 | if a[i] < min { 153 | min = a[i] 154 | } 155 | } 156 | return min 157 | } 158 | 159 | // maxSliceElement will the biggest value of the slice 160 | // Assumptions the assembly can make: 161 | // a != nil 162 | // len(a) > 0 163 | func maxSliceElement(a []float64) float64 { 164 | max := a[0] 165 | for i := 1; i < len(a); i++ { 166 | if a[i] > max { 167 | max = a[i] 168 | } 169 | } 170 | return max 171 | } 172 | 173 | // sliceSum will return the sum of all elements of the slice 174 | // Assumptions the assembly can make: 175 | // a != nil 176 | // len(a) >= 0 177 | func sliceSum(a []float64) float64 { 178 | sum := float64(0.0) 179 | for _, v := range a { 180 | sum += v 181 | } 182 | return sum 183 | } 184 | 185 | // absSlice will return math.Abs(values) of the array 186 | // Assumptions the assembly can make: 187 | // out != nil, a != nil 188 | // len(out) == len(a) 189 | func absSlice(out, a []float64) { 190 | for i, v := range a { 191 | out[i] = float64(math.Abs(float64(v))) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /na64/arrayfuncs_amd64.go: -------------------------------------------------------------------------------- 1 | // +build amd64 2 | 3 | package na64 4 | 5 | import "math" 6 | 7 | // These are function definitions for AMD64 optimized routines, 8 | // and fallback that can be used for performance testing. 9 | // See function documentation in arrayfuncs.go 10 | 11 | // approx 2x faster than Go 12 | func divSlice(out, a, b []float64) 13 | 14 | func divSliceGo(out, a, b []float64) { 15 | for i := 0; i < len(out); i++ { 16 | out[i] = a[i] / b[i] 17 | } 18 | } 19 | 20 | // approx 3x faster than Go 21 | func addSlice(out, a, b []float64) 22 | 23 | func addSliceGo(out, a, b []float64) { 24 | for i := 0; i < len(out); i++ { 25 | out[i] = a[i] + b[i] 26 | } 27 | } 28 | 29 | // approx 3x faster than Go 30 | func mulSlice(out, a, b []float64) 31 | 32 | func mulSliceGo(out, a, b []float64) { 33 | for i := 0; i < len(out); i++ { 34 | out[i] = a[i] * b[i] 35 | } 36 | } 37 | 38 | // approx 3x faster than Go 39 | func subSlice(out, a, b []float64) 40 | 41 | func subSliceGo(out, a, b []float64) { 42 | for i := 0; i < len(out); i++ { 43 | out[i] = a[i] - b[i] 44 | } 45 | } 46 | 47 | // approx 4x faster than Go 48 | func minSlice(out, a, b []float64) 49 | 50 | func minSliceGo(out, a, b []float64) { 51 | for i := 0; i < len(out); i++ { 52 | if a[i] < b[i] { 53 | out[i] = a[i] 54 | } else { 55 | out[i] = b[i] 56 | } 57 | } 58 | } 59 | 60 | // approx 4x faster than Go 61 | func maxSlice(out, a, b []float64) 62 | 63 | func maxSliceGo(out, a, b []float64) { 64 | for i := 0; i < len(out); i++ { 65 | if a[i] > b[i] { 66 | out[i] = a[i] 67 | } else { 68 | out[i] = b[i] 69 | } 70 | } 71 | } 72 | 73 | // approx Xx faster than Go 74 | func csignSlice(out, a, b []float64) 75 | 76 | func csignSliceGo(out, a, b []float64) { 77 | const sign = 1 << 63 78 | for i := 0; i < len(out); i++ { 79 | out[i] = math.Float64frombits(math.Float64bits(a[i])&^sign | math.Float64bits(b[i])&sign) 80 | } 81 | } 82 | 83 | // approx 2x faster than Go 84 | func cdivSlice(out, a []float64, c float64) 85 | 86 | func cdivSliceGo(out, a []float64, c float64) { 87 | for i := 0; i < len(out); i++ { 88 | out[i] = c / a[i] 89 | } 90 | } 91 | 92 | // approx 3x faster than Go 93 | func cmulSlice(out, a []float64, c float64) 94 | 95 | func cmulSliceGo(out, a []float64, c float64) { 96 | for i := 0; i < len(out); i++ { 97 | out[i] = c * a[i] 98 | } 99 | } 100 | 101 | // approx 3x faster than Go 102 | func caddSlice(out, a []float64, c float64) 103 | 104 | func caddSliceGo(out, a []float64, c float64) { 105 | for i := 0; i < len(out); i++ { 106 | out[i] = c * a[i] 107 | } 108 | } 109 | 110 | // approx 3x faster than Go 111 | func addScaledSlice(y, x []float64, a float64) 112 | 113 | func addScaledSliceGo(y, x []float64, a float64) { 114 | for i, v := range x { 115 | y[i] += v * a 116 | } 117 | } 118 | 119 | // approx 2x faster than Go 120 | func sqrtSlice(out, a []float64) 121 | 122 | func sqrtSliceGo(out, a []float64) { 123 | for i := 0; i < len(out); i++ { 124 | out[i] = math.Sqrt(a[i]) 125 | } 126 | } 127 | 128 | // approx 12x faster than Go 129 | func absSlice(out, a []float64) 130 | 131 | func absSliceGo(out, a []float64) { 132 | for i, v := range a { 133 | out[i] = math.Abs(v) 134 | } 135 | } 136 | 137 | // approx 6x faster than Go 138 | func minSliceElement(a []float64) float64 139 | 140 | func minSliceElementGo(a []float64) float64 { 141 | min := math.MaxFloat64 142 | for i := 0; i < len(a); i++ { 143 | if a[i] < min { 144 | min = a[i] 145 | } 146 | } 147 | return min 148 | } 149 | 150 | // approx 6x faster than Go 151 | func maxSliceElement(a []float64) float64 152 | 153 | func maxSliceElementGo(a []float64) float64 { 154 | max := -math.MaxFloat64 155 | for i := 0; i < len(a); i++ { 156 | if a[i] > max { 157 | max = a[i] 158 | } 159 | } 160 | return max 161 | } 162 | 163 | // approx 4x faster than Go 164 | func sliceSum(a []float64) float64 165 | 166 | func sliceSumGo(a []float64) float64 { 167 | sum := 0.0 168 | for _, v := range a { 169 | sum += v 170 | } 171 | return sum 172 | } 173 | -------------------------------------------------------------------------------- /na64/arrayfuncs_amd64.s: -------------------------------------------------------------------------------- 1 | 2 | // func divSlice(out []float64, a []float64, b []float64) 3 | TEXT ·divSlice(SB), 7, $0 4 | MOVQ out+0(FP),SI // SI: &out 5 | MOVQ out_len+8(FP),DX // DX: len(out) 6 | MOVQ a+24(FP),R11 // R11: &a 7 | MOVQ b+48(FP),R9 // R9: &b 8 | MOVQ DX, R10 // R10: len(out) 9 | SHRQ $2, DX // DX: len(out) / 4 10 | ANDQ $3, R10 // R10: len(out) % 4 11 | CMPQ DX ,$0 12 | JEQ remain_div 13 | loopback_div: 14 | MOVUPD (R11),X0 15 | MOVUPD (R9),X1 16 | DIVPD X1,X0 17 | MOVUPD 16(R11),X2 18 | MOVUPD 16(R9),X3 19 | DIVPD X3,X2 20 | MOVUPD X0,(SI) 21 | MOVUPD X2,16(SI) 22 | ADDQ $32, R11 23 | ADDQ $32, R9 24 | ADDQ $32, SI 25 | SUBQ $1,DX 26 | JNZ loopback_div 27 | remain_div: 28 | CMPQ R10,$0 29 | JEQ done_div 30 | onemore_div: 31 | MOVSD (R11),X0 32 | MOVSD (R9),X1 33 | DIVSD X1,X0 34 | MOVSD X0,(SI) 35 | ADDQ $8, R11 36 | ADDQ $8, R9 37 | ADDQ $8, SI 38 | SUBQ $1, R10 39 | JNZ onemore_div 40 | done_div: 41 | RET 42 | 43 | 44 | // func subSlice(out []float64, a []float64, b []float64) 45 | TEXT ·subSlice(SB), 7, $0 46 | MOVQ out+0(FP),SI // SI: &out 47 | MOVQ out_len+8(FP),DX // DX: len(out) 48 | MOVQ a+24(FP),R11 // R11: &a 49 | MOVQ b+48(FP),R9 // R9: &b 50 | MOVQ DX, R10 // R10: len(out) 51 | SHRQ $2, DX // DX: len(out) / 4 52 | ANDQ $3, R10 // R10: len(out) % 4 53 | CMPQ DX ,$0 54 | JEQ remain_sub 55 | loopback_sub: 56 | MOVUPD (R11),X0 57 | MOVUPD (R9),X1 58 | SUBPD X1,X0 59 | MOVUPD 16(R11),X2 60 | MOVUPD 16(R9),X3 61 | SUBPD X3,X2 62 | MOVUPD X0,(SI) 63 | MOVUPD X2,16(SI) 64 | ADDQ $32, R11 65 | ADDQ $32, R9 66 | ADDQ $32, SI 67 | SUBQ $1,DX 68 | JNZ loopback_sub 69 | remain_sub: 70 | CMPQ R10,$0 71 | JEQ done_sub 72 | onemore_sub: 73 | MOVSD (R11),X0 74 | MOVSD (R9),X1 75 | SUBSD X1,X0 76 | MOVSD X0,(SI) 77 | ADDQ $8, R11 78 | ADDQ $8, R9 79 | ADDQ $8, SI 80 | SUBQ $1, R10 81 | JNZ onemore_sub 82 | done_sub: 83 | RET 84 | 85 | // func mulSlice(out []float64, a []float64, b []float64) 86 | TEXT ·mulSlice(SB), 7, $0 87 | MOVQ out(FP),SI // SI: &out 88 | MOVQ out_len+8(FP),DX // DX: len(out) 89 | MOVQ a+24(FP),R11 // R11: &a 90 | MOVQ b+48(FP),R9 // R9: &b 91 | MOVQ DX, R10 // R10: len(out) 92 | SHRQ $2, DX // DX: len(out) / 4 93 | ANDQ $3, R10 // R10: len(out) % 4 94 | CMPQ DX ,$0 95 | JEQ remain_mul 96 | loopback_mul: 97 | MOVUPD (R11),X0 98 | MOVUPD (R9),X1 99 | MULPD X1,X0 100 | MOVUPD 16(R11),X2 101 | MOVUPD 16(R9),X3 102 | MULPD X3,X2 103 | MOVUPD X0,(SI) 104 | MOVUPD X2,16(SI) 105 | ADDQ $32, R11 106 | ADDQ $32, R9 107 | ADDQ $32, SI 108 | SUBQ $1,DX 109 | JNZ loopback_mul 110 | remain_mul: 111 | CMPQ R10,$0 112 | JEQ done_mul 113 | onemore_mul: 114 | MOVSD (R11),X0 115 | MOVSD (R9),X1 116 | MULSD X1,X0 117 | MOVSD X0,(SI) 118 | ADDQ $8, R11 119 | ADDQ $8, R9 120 | ADDQ $8, SI 121 | SUBQ $1, R10 122 | JNZ onemore_mul 123 | done_mul: 124 | RET 125 | 126 | 127 | // func addSlice(out []float64, a []float64, b []float64) 128 | TEXT ·addSlice(SB), 7, $0 129 | MOVQ out(FP),SI // SI: &out 130 | MOVQ out_len+8(FP),DX // DX: len(out) 131 | MOVQ a+24(FP),R11 // R11: &a 132 | MOVQ b+48(FP),R9 // R9: &b 133 | MOVQ DX, R10 // R10: len(out) 134 | SHRQ $2, DX // DX: len(out) / 4 135 | ANDQ $3, R10 // R10: len(out) % 4 136 | CMPQ DX ,$0 137 | JEQ remain_add 138 | loopback_add: 139 | MOVUPD (R11),X0 140 | MOVUPD (R9),X1 141 | ADDPD X1,X0 142 | MOVUPD 16(R11),X2 143 | MOVUPD 16(R9),X3 144 | ADDPD X3,X2 145 | MOVUPD X0,(SI) 146 | MOVUPD X2,16(SI) 147 | ADDQ $32, R11 148 | ADDQ $32, R9 149 | ADDQ $32, SI 150 | SUBQ $1,DX 151 | JNZ loopback_add 152 | remain_add: 153 | CMPQ R10,$0 154 | JEQ done_add 155 | onemore_add: 156 | MOVSD (R11),X0 157 | MOVSD (R9),X1 158 | ADDSD X1,X0 159 | MOVSD X0,(SI) 160 | ADDQ $8, R11 161 | ADDQ $8, R9 162 | ADDQ $8, SI 163 | SUBQ $1, R10 164 | JNZ onemore_add 165 | done_add: 166 | RET 167 | 168 | // func minSlice(out []float64, a []float64, b []float64) 169 | TEXT ·minSlice(SB), 7, $0 170 | MOVQ out(FP),SI // SI: &out 171 | MOVQ out_len+8(FP),DX // DX: len(out) 172 | MOVQ a+24(FP),R11 // R11: &a 173 | MOVQ b+48(FP),R9 // R9: &b 174 | MOVQ DX, R10 // R10: len(out) 175 | SHRQ $2, DX // DX: len(out) / 4 176 | ANDQ $3, R10 // R10: len(out) % 4 177 | CMPQ DX ,$0 178 | JEQ remain_min 179 | loopback_min: 180 | MOVUPD (R11),X0 181 | MOVUPD (R9),X1 182 | MINPD X1,X0 183 | MOVUPD 16(R11),X2 184 | MOVUPD 16(R9),X3 185 | MINPD X3,X2 186 | MOVUPD X0,(SI) 187 | MOVUPD X2,16(SI) 188 | ADDQ $32, R11 189 | ADDQ $32, R9 190 | ADDQ $32, SI 191 | SUBQ $1,DX 192 | JNZ loopback_min 193 | remain_min: 194 | CMPQ R10,$0 195 | JEQ done_min 196 | onemore_min: 197 | MOVSD (R11),X0 198 | MOVSD (R9),X1 199 | MINSD X1,X0 200 | MOVSD X0,(SI) 201 | ADDQ $8, R11 202 | ADDQ $8, R9 203 | ADDQ $8, SI 204 | SUBQ $1, R10 205 | JNZ onemore_min 206 | done_min: 207 | RET 208 | 209 | // func maxSlice(out []float64, a []float64, b []float64) 210 | TEXT ·maxSlice(SB), 7, $0 211 | MOVQ out(FP),SI // SI: &out 212 | MOVQ out_len+8(FP),DX // DX: len(out) 213 | MOVQ a+24(FP),R11 // R11: &a 214 | MOVQ b+48(FP),R9 // R9: &b 215 | MOVQ DX, R10 // R10: len(out) 216 | SHRQ $2, DX // DX: len(out) / 4 217 | ANDQ $3, R10 // R10: len(out) % 4 218 | CMPQ DX ,$0 219 | JEQ remain_max 220 | loopback_max: 221 | MOVUPD (R11),X0 222 | MOVUPD (R9),X1 223 | MAXPD X1,X0 224 | MOVUPD 16(R11),X2 225 | MOVUPD 16(R9),X3 226 | MAXPD X3,X2 227 | MOVUPD X0,(SI) 228 | MOVUPD X2,16(SI) 229 | ADDQ $32, R11 230 | ADDQ $32, R9 231 | ADDQ $32, SI 232 | SUBQ $1,DX 233 | JNZ loopback_max 234 | remain_max: 235 | CMPQ R10,$0 236 | JEQ done_max 237 | onemore_max: 238 | MOVSD (R11),X0 239 | MOVSD (R9),X1 240 | MAXSD X1,X0 241 | MOVSD X0,(SI) 242 | ADDQ $8, R11 243 | ADDQ $8, R9 244 | ADDQ $8, SI 245 | SUBQ $1, R10 246 | JNZ onemore_max 247 | done_max: 248 | RET 249 | 250 | // func csignSlice(out []float64, a []float64, b []float64) 251 | TEXT ·csignSlice(SB), 7, $0 252 | MOVQ out(FP),SI // SI: &out 253 | MOVQ out_len+8(FP),DX // DX: len(out) 254 | MOVQ a+24(FP),R11 // R11: &a 255 | MOVQ b+48(FP),R9 // R9: &b 256 | MOVQ DX, R10 // R10: len(out) 257 | MOVQ $(1<<63), BX 258 | MOVQ BX, X4 // X4: Sign 259 | UNPCKLPD X4, X4 260 | SHRQ $2, DX // DX: len(out) / 4 261 | ANDQ $3, R10 // R10: len(out) % 4 262 | CMPQ DX ,$0 263 | JEQ remain_csign 264 | loopback_csign: 265 | MOVAPD X4, X5 266 | MOVAPD X4, X6 267 | MOVUPD (R11),X0 268 | MOVUPD (R9),X1 269 | MOVUPD 16(R11),X2 270 | MOVUPD 16(R9),X3 271 | ANDNPD X0, X5 272 | ANDPD X4, X1 273 | ORPD X5, X1 274 | 275 | ANDNPD X2, X6 276 | ANDPD X4, X3 277 | ORPD X6, X3 278 | MOVUPD X1,(SI) 279 | MOVUPD X3,16(SI) 280 | ADDQ $32, R11 281 | ADDQ $32, R9 282 | ADDQ $32, SI 283 | SUBQ $1,DX 284 | JNZ loopback_csign 285 | remain_csign: 286 | CMPQ R10,$0 287 | JEQ done_csign 288 | onemore_csign: 289 | MOVSD X4, X5 290 | MOVSD (R11),X0 291 | MOVSD (R9),X1 292 | ANDNPD X0, X5 293 | ANDPD X4, X1 294 | ORPD X5, X1 295 | MOVSD X1,(SI) 296 | ADDQ $8, R11 297 | ADDQ $8, R9 298 | ADDQ $8, SI 299 | SUBQ $1, R10 300 | JNZ onemore_csign 301 | done_csign: 302 | RET 303 | 304 | // func cdivSlice(out []float64, a []float64, c float64) 305 | TEXT ·cdivSlice(SB), 7, $0 306 | MOVQ out(FP),SI // SI: &out 307 | MOVQ out_len+8(FP),DX // DX: len(out) 308 | MOVQ a+24(FP),R11 // R11: &a 309 | MOVSD c+48(FP),X4 // X4: c 310 | MOVQ DX, R10 // R10: len(out) 311 | SHRQ $2, DX // DX: len(out) / 4 312 | ANDQ $3, R10 // R10: len(out) % 4 313 | CMPQ DX ,$0 314 | JEQ remain_cdiv 315 | UNPCKLPD X4, X4 316 | loopback_cdiv: 317 | MOVAPD X4, X1 318 | MOVAPD X4, X3 319 | MOVUPD (R11),X0 320 | DIVPD X0,X1 321 | MOVUPD 16(R11),X2 322 | DIVPD X2,X3 323 | MOVUPD X1,(SI) 324 | MOVUPD X3,16(SI) 325 | ADDQ $32, R11 326 | ADDQ $32, SI 327 | SUBQ $1,DX 328 | JNZ loopback_cdiv 329 | remain_cdiv: 330 | CMPQ R10,$0 331 | JEQ done_cdiv 332 | onemore_cdiv: 333 | MOVAPD X4, X1 334 | MOVSD (R11),X0 335 | DIVSD X0,X1 336 | MOVSD X1,(SI) 337 | ADDQ $8, R11 338 | ADDQ $8, SI 339 | SUBQ $1, R10 340 | JNZ onemore_cdiv 341 | done_cdiv: 342 | RET 343 | 344 | 345 | // func cmulSlice(out []float64, a []float64, c float64) 346 | TEXT ·cmulSlice(SB), 7, $0 347 | MOVQ out(FP),SI // SI: &out 348 | MOVQ out_len+8(FP),DX // DX: len(out) 349 | MOVQ a+24(FP),R11 // R11: &a 350 | MOVSD c+48(FP),X4 // X4: c 351 | MOVQ DX, R10 // R10: len(out) 352 | SHRQ $2, DX // DX: len(out) / 4 353 | ANDQ $3, R10 // R10: len(out) % 4 354 | CMPQ DX ,$0 355 | JEQ remain_cmul 356 | UNPCKLPD X4, X4 357 | loopback_cmul: 358 | MOVUPD (R11),X0 359 | MULPD X4,X0 360 | MOVUPD 16(R11),X2 361 | MULPD X4,X2 362 | MOVUPD X0,(SI) 363 | MOVUPD X2,16(SI) 364 | ADDQ $32, R11 365 | ADDQ $32, SI 366 | SUBQ $1,DX 367 | JNZ loopback_cmul 368 | remain_cmul: 369 | CMPQ R10,$0 370 | JEQ done_cmul 371 | onemore_cmul: 372 | MOVSD (R11),X0 373 | MULSD X4,X0 374 | MOVSD X0,(SI) 375 | ADDQ $8, R11 376 | ADDQ $8, SI 377 | SUBQ $1, R10 378 | JNZ onemore_cmul 379 | done_cmul: 380 | RET 381 | 382 | 383 | // func caddSlice(out []float64, a []float64, c float64) 384 | TEXT ·caddSlice(SB), 7, $0 385 | MOVQ out(FP),SI // SI: &out 386 | MOVQ out_len+8(FP),DX // DX: len(out) 387 | MOVQ a+24(FP),R11 // R11: &a 388 | MOVSD c+48(FP),X4 // X4: c 389 | MOVQ DX, R10 // R10: len(out) 390 | SHRQ $2, DX // DX: len(out) / 4 391 | ANDQ $3, R10 // R10: len(out) % 4 392 | CMPQ DX ,$0 393 | JEQ remain_cadd 394 | UNPCKLPD X4, X4 395 | loopback_cadd: 396 | MOVUPD (R11),X0 397 | ADDPD X4,X0 398 | MOVUPD 16(R11),X2 399 | ADDPD X4,X2 400 | MOVUPD X0,(SI) 401 | MOVUPD X2,16(SI) 402 | ADDQ $32, R11 403 | ADDQ $32, SI 404 | SUBQ $1,DX 405 | JNZ loopback_cadd 406 | remain_cadd: 407 | CMPQ R10,$0 408 | JEQ done_cadd 409 | onemore_cadd: 410 | MOVSD (R11),X0 411 | ADDSD X4,X0 412 | MOVSD X0,(SI) 413 | ADDQ $8, R11 414 | ADDQ $8, SI 415 | SUBQ $1, R10 416 | JNZ onemore_cadd 417 | done_cadd: 418 | RET 419 | 420 | 421 | // func addScaledSlice(y []float64, x []float64, a float64) 422 | TEXT ·addScaledSlice(SB), 7, $0 423 | MOVQ y(FP),SI // SI: &y 424 | MOVQ y_len+8(FP),DX // DX: len(y) 425 | MOVQ x+24(FP),R11 // R11: &x 426 | MOVSD a+48(FP),X4 // X4: a 427 | MOVQ DX, R10 // R10: len(y) 428 | SHRQ $2, DX // DX: len(y) / 4 429 | ANDQ $3, R10 // R10: len(y) % 4 430 | CMPQ DX ,$0 431 | JEQ remain_madd 432 | UNPCKLPD X4, X4 433 | loopback_madd: 434 | MOVUPD (R11),X0 435 | MOVUPD (SI), X5 436 | MULPD X4, X0 437 | ADDPD X5, X0 438 | MOVUPD 16(R11),X2 439 | MOVUPD 16(SI), X6 440 | MULPD X4, X2 441 | ADDPD X6, X2 442 | MOVUPD X0,(SI) 443 | MOVUPD X2,16(SI) 444 | ADDQ $32, R11 445 | ADDQ $32, SI 446 | SUBQ $1,DX 447 | JNZ loopback_madd 448 | remain_madd: 449 | CMPQ R10,$0 450 | JEQ done_madd 451 | onemore_madd: 452 | MOVSD (R11),X0 453 | MOVSD (SI),X1 454 | MULSD X4,X0 455 | ADDSD X1,X0 456 | MOVSD X0,(SI) 457 | ADDQ $8, R11 458 | ADDQ $8, SI 459 | SUBQ $1, R10 460 | JNZ onemore_madd 461 | done_madd: 462 | RET 463 | 464 | // func sqrtSlice(out []float64, a []float64) 465 | TEXT ·sqrtSlice(SB), 7, $0 466 | MOVQ out(FP),SI // SI: &out 467 | MOVQ out_len+8(FP),DX // DX: len(out) 468 | MOVQ a+24(FP),R11 // R11: &a 469 | MOVQ DX, R10 // R10: len(out) 470 | SHRQ $2, DX // DX: len(out) / 4 471 | ANDQ $3, R10 // R10: len(out) % 4 472 | CMPQ DX ,$0 473 | JEQ remain_sqrt 474 | loopback_sqrt: 475 | MOVUPD (R11),X0 476 | SQRTPD X0,X0 477 | MOVUPD 16(R11),X2 478 | SQRTPD X2,X2 479 | MOVUPD X0,(SI) 480 | MOVUPD X2,16(SI) 481 | ADDQ $32, R11 482 | ADDQ $32, SI 483 | SUBQ $1,DX 484 | JNZ loopback_sqrt 485 | remain_sqrt: 486 | CMPQ R10,$0 487 | JEQ done_sqrt 488 | onemore_sqrt: 489 | MOVSD (R11),X0 490 | SQRTSD X0,X0 491 | MOVSD X0,(SI) 492 | ADDQ $8, R11 493 | ADDQ $8, SI 494 | SUBQ $1, R10 495 | JNZ onemore_sqrt 496 | done_sqrt: 497 | RET 498 | 499 | // func absSlice(out []float64, a []float64) 500 | TEXT ·absSlice(SB), 7, $0 501 | MOVQ out(FP),SI // SI: &out 502 | MOVQ out_len+8(FP),DX // DX: len(out) 503 | MOVQ a+24(FP),R11 // R11: &a 504 | MOVQ $(1<<63), BX 505 | MOVQ BX, X5 // X1: Sign 506 | UNPCKLPD X5, X5 507 | MOVQ DX, R10 // R10: len(out) 508 | SHRQ $2, DX // DX: len(out) / 4 509 | ANDQ $3, R10 // R10: len(out) % 4 510 | CMPQ DX ,$0 511 | JEQ remain_abs 512 | loopback_abs: 513 | MOVAPD X5, X3 514 | MOVAPD X5, X4 515 | MOVUPD (R11),X0 516 | ANDNPD X0, X3 517 | MOVUPD 16(R11),X1 518 | ANDNPD X1, X4 519 | MOVUPD X3,(SI) 520 | MOVUPD X4,16(SI) 521 | ADDQ $32, R11 522 | ADDQ $32, SI 523 | SUBQ $1,DX 524 | JNZ loopback_abs 525 | remain_abs: 526 | CMPQ R10,$0 527 | JEQ done_abs 528 | onemore_abs: 529 | MOVAPS X5, X1 530 | MOVSD (R11),X0 531 | ANDNPD X0, X1 532 | MOVSD X1,(SI) 533 | ADDQ $8, R11 534 | ADDQ $8, SI 535 | SUBQ $1, R10 536 | JNZ onemore_abs 537 | done_abs: 538 | RET 539 | 540 | // func minSliceElement(a []float64) float64 541 | TEXT ·minSliceElement(SB), 7, $0 542 | MOVQ a(FP),SI // SI: &a 543 | MOVQ a_len+8(FP),DX // DX: len(a) 544 | MOVSD (SI), X0 // Initial value 545 | ADDQ $8, SI 546 | SUBQ $1, DX 547 | 548 | UNPCKLPD X0, X0 549 | MOVQ DX, R10 // R10: len(out) -1 550 | SHRQ $2, DX // DX: (len(out) - 1) / 4 551 | ANDQ $3, R10 // R10: (len(out) -1 ) % 4 552 | MOVAPD X0, X1 553 | CMPQ DX ,$0 554 | JEQ remain_min_e 555 | next_min_e: 556 | MOVUPD (SI), X2 557 | MOVUPD 16(SI), X3 558 | MINPD X2, X0 559 | MINPD X3, X1 560 | ADDQ $32, SI 561 | SUBQ $1, DX 562 | JNZ next_min_e 563 | CMPQ R10, $0 564 | JZ done_min_e 565 | remain_min_e: 566 | MOVSD (SI), X2 567 | MINSD X2, X0 568 | ADDQ $8, SI 569 | SUBQ $1, R10 570 | JNZ remain_min_e 571 | done_min_e: 572 | MINPD X1, X0 573 | MOVAPD X0, X2 574 | UNPCKHPD X0, X2 575 | MINSD X2, X0 576 | MOVSD X0, ret+24(FP) 577 | RET 578 | 579 | 580 | // func maxSliceElement(a []float64) float64 581 | TEXT ·maxSliceElement(SB), 7, $0 582 | MOVQ a(FP),SI // SI: &a 583 | MOVQ a_len+8(FP),DX // DX: len(a) 584 | MOVSD (SI), X0 // Initial value 585 | ADDQ $8, SI 586 | SUBQ $1, DX 587 | 588 | UNPCKLPD X0, X0 589 | MOVQ DX, R10 // R10: len(out) -1 590 | SHRQ $2, DX // DX: (len(out) - 1) / 4 591 | ANDQ $3, R10 // R10: (len(out) -1 ) % 4 592 | MOVAPD X0, X1 593 | CMPQ DX ,$0 594 | JEQ remain_max_e 595 | next_max_e: 596 | MOVUPD (SI), X2 597 | MOVUPD 16(SI), X3 598 | MAXPD X2, X0 599 | MAXPD X3, X1 600 | ADDQ $32, SI 601 | SUBQ $1, DX 602 | JNZ next_max_e 603 | CMPQ R10, $0 604 | JZ done_max_e 605 | remain_max_e: 606 | MOVSD (SI), X2 607 | MAXSD X2, X0 608 | ADDQ $8, SI 609 | SUBQ $1, R10 610 | JNZ remain_max_e 611 | done_max_e: 612 | MAXPD X1, X0 613 | MOVAPD X0, X2 614 | UNPCKHPD X0, X2 615 | MAXSD X2, X0 616 | MOVSD X0, ret+24(FP) 617 | RET 618 | 619 | 620 | 621 | // func sliceSum(a []float64) float64 622 | TEXT ·sliceSum(SB), 7, $0 623 | MOVQ a(FP),SI // SI: &a 624 | MOVQ a_len+8(FP),DX // DX: len(a) 625 | XORPD X0, X0 // Initial value 626 | XORPD X1, X1 // Initial value 627 | 628 | MOVQ DX, R10 // R10: len(out) -1 629 | SHRQ $2, DX // DX: (len(out) - 1) / 4 630 | ANDQ $3, R10 // R10: (len(out) -1 ) % 4 631 | CMPQ DX ,$0 632 | JEQ remain_sum 633 | next_sum: 634 | MOVUPD (SI), X2 635 | MOVUPD 16(SI), X3 636 | ADDPD X2, X0 637 | ADDPD X3, X1 638 | ADDQ $32, SI 639 | SUBQ $1, DX 640 | JNZ next_sum 641 | CMPQ R10, $0 642 | JZ done_sum 643 | remain_sum: 644 | MOVSD (SI), X2 645 | ADDSD X2, X0 646 | ADDQ $8, SI 647 | SUBQ $1, R10 648 | JNZ remain_sum 649 | done_sum: 650 | ADDPD X1, X0 651 | MOVAPD X0, X2 652 | UNPCKHPD X0, X2 653 | ADDSD X2, X0 654 | MOVSD X0, ret+24(FP) 655 | RET 656 | 657 | -------------------------------------------------------------------------------- /na64/gonum.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package na64 9 | 10 | // Matrix is as an NArray of rank 2 that satisfies the gonum Matrix interfaces. 11 | type Matrix NArray 12 | 13 | // Vector is as an NArray of rank 1 that satisfies the gonum Vectorer interface. 14 | type Vector NArray 15 | 16 | // Matrix creates a subarray of rank 2. 17 | // Equivalent to SubArray but restricted to the case where the 18 | // resulting subarray has rank=2. (It will panic otherwise.) 19 | // See SubArray for details. 20 | func (na *NArray) Matrix(query ...int) *Matrix { 21 | 22 | mat := na.SubArray(query...) 23 | if len(mat.Shape) != 2 { 24 | panic("matrix must have rank equal two") 25 | } 26 | return (*Matrix)(mat) 27 | } 28 | 29 | // Dims returns the dimensions of a Matrix. 30 | func (mat *Matrix) Dims() (r, c int) { 31 | na := (*NArray)(mat) 32 | return na.Shape[0], na.Shape[1] 33 | } 34 | 35 | // At returns the value of a matrix element at (r, c). It will panic if r or c are 36 | // out of bounds for the matrix. 37 | func (mat *Matrix) At(r, c int) float64 { 38 | na := (*NArray)(mat) 39 | return na.At(r, c) 40 | } 41 | 42 | // Set alters the matrix element at (r, c) to v. It will panic if r or c are out of 43 | // bounds for the matrix. 44 | func (mat *Matrix) Set(r, c int, v float64) { 45 | na := (*NArray)(mat) 46 | na.Set(v, r, c) 47 | } 48 | 49 | // String returns vector as a printable string. 50 | func (mat *Matrix) String() string { 51 | na := (*NArray)(mat) 52 | return na.String() 53 | } 54 | 55 | // Vector creates a subarray of rank 1. 56 | // Equivalent to SubArray but restricted to the case where the 57 | // resulting subarray has rank=1. (It will panic otherwise.) 58 | // See SubArray for details. 59 | // 60 | // Example, given a 5x10 matrix (rank=2), return the vector 61 | // of dim 10 for row idx=3: 62 | // 63 | // x := New(5,10) 64 | // y := x.Vector(3,-1) 65 | // // y = {x_30, x_31, ... , x_39} 66 | func (na *NArray) Vector(query ...int) *Vector { 67 | 68 | vec := na.SubArray(query...) 69 | if len(vec.Shape) != 1 { 70 | panic("vector must have rank equal one") 71 | } 72 | return (*Vector)(vec) 73 | } 74 | 75 | // Row returns a slice of float64 for the row specified. It will panic if the index 76 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 77 | // returned, if it is not nil the number of elements copied will be the minimum of the 78 | // length of the slice and the number of columns in the matrix. 79 | func (mat *Matrix) Row(dst []float64, i int) []float64 { 80 | _, ncols := mat.Dims() 81 | if dst == nil { 82 | dst = make([]float64, ncols, ncols) 83 | } 84 | for j, _ := range dst { 85 | dst[j] = mat.At(i, j) 86 | } 87 | return dst 88 | } 89 | 90 | // Col returns a slice of float64 for the column specified. It will panic if the index 91 | // is out of bounds. If the call requires a copy and dst is not nil it will be used and 92 | // returned, if it is not nil the number of elements copied will be the minimum of the 93 | // length of the slice and the number of rows in the matrix. 94 | func (mat *Matrix) Col(dst []float64, j int) []float64 { 95 | nrows, _ := mat.Dims() 96 | if dst == nil { 97 | dst = make([]float64, nrows, nrows) 98 | } 99 | for i, _ := range dst { 100 | dst[i] = mat.At(i, j) 101 | } 102 | return dst 103 | } 104 | 105 | // SetRow sets the values of the specified row to the values held in a slice of float64. 106 | // It will panic if the index is out of bounds. The number of elements copied is 107 | // returned and will be the minimum of the length of the slice and the number of columns 108 | // in the matrix. 109 | func (mat *Matrix) SetRow(i int, src []float64) int { 110 | 111 | numCopied := len(src) 112 | if len(src) > mat.Shape[1] { 113 | numCopied = mat.Shape[1] 114 | } 115 | for j := 0; j < numCopied; j++ { 116 | mat.Set(i, j, src[j]) 117 | } 118 | return numCopied 119 | } 120 | 121 | // SetCol sets the values of the specified column to the values held in a slice of float64. 122 | // It will panic if the index is out of bounds. The number of elements copied is 123 | // returned and will be the minimum of the length of the slice and the number of rows 124 | // in the matrix. 125 | func (mat *Matrix) SetCol(j int, src []float64) int { 126 | 127 | numCopied := len(src) 128 | if len(src) > mat.Shape[0] { 129 | numCopied = mat.Shape[0] 130 | } 131 | for i := 0; i < numCopied; i++ { 132 | mat.Set(i, j, src[i]) 133 | } 134 | return numCopied 135 | } 136 | 137 | // TODO finish implementing gonum interfaces. 138 | -------------------------------------------------------------------------------- /na64/gonum_test.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | package na64 9 | 10 | import "testing" 11 | 12 | func TestMatrix(t *testing.T) { 13 | 14 | mat := na234.Matrix(0, -1, -1) 15 | t.Log(mat) 16 | 17 | row0 := mat.Row(nil, 0) 18 | row1 := mat.Row(nil, 1) 19 | row2 := mat.Row(nil, 2) 20 | col0 := mat.Col(nil, 0) 21 | col1 := mat.Col(nil, 1) 22 | col2 := mat.Col(nil, 2) 23 | col3 := mat.Col(nil, 3) 24 | 25 | t.Log(row0) 26 | t.Log(row1) 27 | t.Log(row2) 28 | t.Log(col0) 29 | t.Log(col1) 30 | t.Log(col2) 31 | t.Log(col3) 32 | 33 | na := na234.SubArray(1, 2, -1) 34 | vec := na234.Vector(1, 2, -1) 35 | if !EqualValues(na, (*NArray)(vec), 0) { 36 | t.Fatalf("expected same values") 37 | } 38 | 39 | na = na234.SubArray(-1, 2, -1) 40 | mat = na234.Matrix(-1, 2, -1) 41 | if !EqualValues(na, (*NArray)(mat), 0) { 42 | t.Fatalf("expected same values") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /na64/math_gen.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | // more info at github.com/akualab/narray 3 | 4 | package na64 5 | 6 | import "math" 7 | 8 | // Acosh applies math.Acosh() elementwise to a multidimensional array. 9 | // See math package in standard lib for details. 10 | // 11 | // If 'out' is nil a new array is created. 12 | // Will panic if 'out' and 'in' shapes don't match. 13 | func Acosh(out, in *NArray) *NArray { 14 | if out == nil { 15 | out = New(in.Shape...) 16 | } else if !EqualShape(out, in) { 17 | panic("Acosh:narrays must have equal shape.") 18 | } 19 | for k, v := range in.Data { 20 | out.Data[k] = float64(math.Acosh(float64(v))) 21 | } 22 | return out 23 | } 24 | 25 | // Asin applies math.Asin() elementwise to a multidimensional array. 26 | // See math package in standard lib for details. 27 | // 28 | // If 'out' is nil a new array is created. 29 | // Will panic if 'out' and 'in' shapes don't match. 30 | func Asin(out, in *NArray) *NArray { 31 | if out == nil { 32 | out = New(in.Shape...) 33 | } else if !EqualShape(out, in) { 34 | panic("Asin:narrays must have equal shape.") 35 | } 36 | for k, v := range in.Data { 37 | out.Data[k] = float64(math.Asin(float64(v))) 38 | } 39 | return out 40 | } 41 | 42 | // Acos applies math.Acos() elementwise to a multidimensional array. 43 | // See math package in standard lib for details. 44 | // 45 | // If 'out' is nil a new array is created. 46 | // Will panic if 'out' and 'in' shapes don't match. 47 | func Acos(out, in *NArray) *NArray { 48 | if out == nil { 49 | out = New(in.Shape...) 50 | } else if !EqualShape(out, in) { 51 | panic("Acos:narrays must have equal shape.") 52 | } 53 | for k, v := range in.Data { 54 | out.Data[k] = float64(math.Acos(float64(v))) 55 | } 56 | return out 57 | } 58 | 59 | // Asinh applies math.Asinh() elementwise to a multidimensional array. 60 | // See math package in standard lib for details. 61 | // 62 | // If 'out' is nil a new array is created. 63 | // Will panic if 'out' and 'in' shapes don't match. 64 | func Asinh(out, in *NArray) *NArray { 65 | if out == nil { 66 | out = New(in.Shape...) 67 | } else if !EqualShape(out, in) { 68 | panic("Asinh:narrays must have equal shape.") 69 | } 70 | for k, v := range in.Data { 71 | out.Data[k] = float64(math.Asinh(float64(v))) 72 | } 73 | return out 74 | } 75 | 76 | // Atan applies math.Atan() elementwise to a multidimensional array. 77 | // See math package in standard lib for details. 78 | // 79 | // If 'out' is nil a new array is created. 80 | // Will panic if 'out' and 'in' shapes don't match. 81 | func Atan(out, in *NArray) *NArray { 82 | if out == nil { 83 | out = New(in.Shape...) 84 | } else if !EqualShape(out, in) { 85 | panic("Atan:narrays must have equal shape.") 86 | } 87 | for k, v := range in.Data { 88 | out.Data[k] = float64(math.Atan(float64(v))) 89 | } 90 | return out 91 | } 92 | 93 | // Atanh applies math.Atanh() elementwise to a multidimensional array. 94 | // See math package in standard lib for details. 95 | // 96 | // If 'out' is nil a new array is created. 97 | // Will panic if 'out' and 'in' shapes don't match. 98 | func Atanh(out, in *NArray) *NArray { 99 | if out == nil { 100 | out = New(in.Shape...) 101 | } else if !EqualShape(out, in) { 102 | panic("Atanh:narrays must have equal shape.") 103 | } 104 | for k, v := range in.Data { 105 | out.Data[k] = float64(math.Atanh(float64(v))) 106 | } 107 | return out 108 | } 109 | 110 | // Cbrt applies math.Cbrt() elementwise to a multidimensional array. 111 | // See math package in standard lib for details. 112 | // 113 | // If 'out' is nil a new array is created. 114 | // Will panic if 'out' and 'in' shapes don't match. 115 | func Cbrt(out, in *NArray) *NArray { 116 | if out == nil { 117 | out = New(in.Shape...) 118 | } else if !EqualShape(out, in) { 119 | panic("Cbrt:narrays must have equal shape.") 120 | } 121 | for k, v := range in.Data { 122 | out.Data[k] = float64(math.Cbrt(float64(v))) 123 | } 124 | return out 125 | } 126 | 127 | // Erf applies math.Erf() elementwise to a multidimensional array. 128 | // See math package in standard lib for details. 129 | // 130 | // If 'out' is nil a new array is created. 131 | // Will panic if 'out' and 'in' shapes don't match. 132 | func Erf(out, in *NArray) *NArray { 133 | if out == nil { 134 | out = New(in.Shape...) 135 | } else if !EqualShape(out, in) { 136 | panic("Erf:narrays must have equal shape.") 137 | } 138 | for k, v := range in.Data { 139 | out.Data[k] = float64(math.Erf(float64(v))) 140 | } 141 | return out 142 | } 143 | 144 | // Erfc applies math.Erfc() elementwise to a multidimensional array. 145 | // See math package in standard lib for details. 146 | // 147 | // If 'out' is nil a new array is created. 148 | // Will panic if 'out' and 'in' shapes don't match. 149 | func Erfc(out, in *NArray) *NArray { 150 | if out == nil { 151 | out = New(in.Shape...) 152 | } else if !EqualShape(out, in) { 153 | panic("Erfc:narrays must have equal shape.") 154 | } 155 | for k, v := range in.Data { 156 | out.Data[k] = float64(math.Erfc(float64(v))) 157 | } 158 | return out 159 | } 160 | 161 | // Exp applies math.Exp() elementwise to a multidimensional array. 162 | // See math package in standard lib for details. 163 | // 164 | // If 'out' is nil a new array is created. 165 | // Will panic if 'out' and 'in' shapes don't match. 166 | func Exp(out, in *NArray) *NArray { 167 | if out == nil { 168 | out = New(in.Shape...) 169 | } else if !EqualShape(out, in) { 170 | panic("Exp:narrays must have equal shape.") 171 | } 172 | for k, v := range in.Data { 173 | out.Data[k] = float64(math.Exp(float64(v))) 174 | } 175 | return out 176 | } 177 | 178 | // Exp2 applies math.Exp2() elementwise to a multidimensional array. 179 | // See math package in standard lib for details. 180 | // 181 | // If 'out' is nil a new array is created. 182 | // Will panic if 'out' and 'in' shapes don't match. 183 | func Exp2(out, in *NArray) *NArray { 184 | if out == nil { 185 | out = New(in.Shape...) 186 | } else if !EqualShape(out, in) { 187 | panic("Exp2:narrays must have equal shape.") 188 | } 189 | for k, v := range in.Data { 190 | out.Data[k] = float64(math.Exp2(float64(v))) 191 | } 192 | return out 193 | } 194 | 195 | // Expm1 applies math.Expm1() elementwise to a multidimensional array. 196 | // See math package in standard lib for details. 197 | // 198 | // If 'out' is nil a new array is created. 199 | // Will panic if 'out' and 'in' shapes don't match. 200 | func Expm1(out, in *NArray) *NArray { 201 | if out == nil { 202 | out = New(in.Shape...) 203 | } else if !EqualShape(out, in) { 204 | panic("Expm1:narrays must have equal shape.") 205 | } 206 | for k, v := range in.Data { 207 | out.Data[k] = float64(math.Expm1(float64(v))) 208 | } 209 | return out 210 | } 211 | 212 | // Floor applies math.Floor() elementwise to a multidimensional array. 213 | // See math package in standard lib for details. 214 | // 215 | // If 'out' is nil a new array is created. 216 | // Will panic if 'out' and 'in' shapes don't match. 217 | func Floor(out, in *NArray) *NArray { 218 | if out == nil { 219 | out = New(in.Shape...) 220 | } else if !EqualShape(out, in) { 221 | panic("Floor:narrays must have equal shape.") 222 | } 223 | for k, v := range in.Data { 224 | out.Data[k] = float64(math.Floor(float64(v))) 225 | } 226 | return out 227 | } 228 | 229 | // Ceil applies math.Ceil() elementwise to a multidimensional array. 230 | // See math package in standard lib for details. 231 | // 232 | // If 'out' is nil a new array is created. 233 | // Will panic if 'out' and 'in' shapes don't match. 234 | func Ceil(out, in *NArray) *NArray { 235 | if out == nil { 236 | out = New(in.Shape...) 237 | } else if !EqualShape(out, in) { 238 | panic("Ceil:narrays must have equal shape.") 239 | } 240 | for k, v := range in.Data { 241 | out.Data[k] = float64(math.Ceil(float64(v))) 242 | } 243 | return out 244 | } 245 | 246 | // Trunc applies math.Trunc() elementwise to a multidimensional array. 247 | // See math package in standard lib for details. 248 | // 249 | // If 'out' is nil a new array is created. 250 | // Will panic if 'out' and 'in' shapes don't match. 251 | func Trunc(out, in *NArray) *NArray { 252 | if out == nil { 253 | out = New(in.Shape...) 254 | } else if !EqualShape(out, in) { 255 | panic("Trunc:narrays must have equal shape.") 256 | } 257 | for k, v := range in.Data { 258 | out.Data[k] = float64(math.Trunc(float64(v))) 259 | } 260 | return out 261 | } 262 | 263 | // Gamma applies math.Gamma() elementwise to a multidimensional array. 264 | // See math package in standard lib for details. 265 | // 266 | // If 'out' is nil a new array is created. 267 | // Will panic if 'out' and 'in' shapes don't match. 268 | func Gamma(out, in *NArray) *NArray { 269 | if out == nil { 270 | out = New(in.Shape...) 271 | } else if !EqualShape(out, in) { 272 | panic("Gamma:narrays must have equal shape.") 273 | } 274 | for k, v := range in.Data { 275 | out.Data[k] = float64(math.Gamma(float64(v))) 276 | } 277 | return out 278 | } 279 | 280 | // J0 applies math.J0() elementwise to a multidimensional array. 281 | // See math package in standard lib for details. 282 | // 283 | // If 'out' is nil a new array is created. 284 | // Will panic if 'out' and 'in' shapes don't match. 285 | func J0(out, in *NArray) *NArray { 286 | if out == nil { 287 | out = New(in.Shape...) 288 | } else if !EqualShape(out, in) { 289 | panic("J0:narrays must have equal shape.") 290 | } 291 | for k, v := range in.Data { 292 | out.Data[k] = float64(math.J0(float64(v))) 293 | } 294 | return out 295 | } 296 | 297 | // Y0 applies math.Y0() elementwise to a multidimensional array. 298 | // See math package in standard lib for details. 299 | // 300 | // If 'out' is nil a new array is created. 301 | // Will panic if 'out' and 'in' shapes don't match. 302 | func Y0(out, in *NArray) *NArray { 303 | if out == nil { 304 | out = New(in.Shape...) 305 | } else if !EqualShape(out, in) { 306 | panic("Y0:narrays must have equal shape.") 307 | } 308 | for k, v := range in.Data { 309 | out.Data[k] = float64(math.Y0(float64(v))) 310 | } 311 | return out 312 | } 313 | 314 | // J1 applies math.J1() elementwise to a multidimensional array. 315 | // See math package in standard lib for details. 316 | // 317 | // If 'out' is nil a new array is created. 318 | // Will panic if 'out' and 'in' shapes don't match. 319 | func J1(out, in *NArray) *NArray { 320 | if out == nil { 321 | out = New(in.Shape...) 322 | } else if !EqualShape(out, in) { 323 | panic("J1:narrays must have equal shape.") 324 | } 325 | for k, v := range in.Data { 326 | out.Data[k] = float64(math.J1(float64(v))) 327 | } 328 | return out 329 | } 330 | 331 | // Y1 applies math.Y1() elementwise to a multidimensional array. 332 | // See math package in standard lib for details. 333 | // 334 | // If 'out' is nil a new array is created. 335 | // Will panic if 'out' and 'in' shapes don't match. 336 | func Y1(out, in *NArray) *NArray { 337 | if out == nil { 338 | out = New(in.Shape...) 339 | } else if !EqualShape(out, in) { 340 | panic("Y1:narrays must have equal shape.") 341 | } 342 | for k, v := range in.Data { 343 | out.Data[k] = float64(math.Y1(float64(v))) 344 | } 345 | return out 346 | } 347 | 348 | // Log applies math.Log() elementwise to a multidimensional array. 349 | // See math package in standard lib for details. 350 | // 351 | // If 'out' is nil a new array is created. 352 | // Will panic if 'out' and 'in' shapes don't match. 353 | func Log(out, in *NArray) *NArray { 354 | if out == nil { 355 | out = New(in.Shape...) 356 | } else if !EqualShape(out, in) { 357 | panic("Log:narrays must have equal shape.") 358 | } 359 | for k, v := range in.Data { 360 | out.Data[k] = float64(math.Log(float64(v))) 361 | } 362 | return out 363 | } 364 | 365 | // Log10 applies math.Log10() elementwise to a multidimensional array. 366 | // See math package in standard lib for details. 367 | // 368 | // If 'out' is nil a new array is created. 369 | // Will panic if 'out' and 'in' shapes don't match. 370 | func Log10(out, in *NArray) *NArray { 371 | if out == nil { 372 | out = New(in.Shape...) 373 | } else if !EqualShape(out, in) { 374 | panic("Log10:narrays must have equal shape.") 375 | } 376 | for k, v := range in.Data { 377 | out.Data[k] = float64(math.Log10(float64(v))) 378 | } 379 | return out 380 | } 381 | 382 | // Log2 applies math.Log2() elementwise to a multidimensional array. 383 | // See math package in standard lib for details. 384 | // 385 | // If 'out' is nil a new array is created. 386 | // Will panic if 'out' and 'in' shapes don't match. 387 | func Log2(out, in *NArray) *NArray { 388 | if out == nil { 389 | out = New(in.Shape...) 390 | } else if !EqualShape(out, in) { 391 | panic("Log2:narrays must have equal shape.") 392 | } 393 | for k, v := range in.Data { 394 | out.Data[k] = float64(math.Log2(float64(v))) 395 | } 396 | return out 397 | } 398 | 399 | // Log1p applies math.Log1p() elementwise to a multidimensional array. 400 | // See math package in standard lib for details. 401 | // 402 | // If 'out' is nil a new array is created. 403 | // Will panic if 'out' and 'in' shapes don't match. 404 | func Log1p(out, in *NArray) *NArray { 405 | if out == nil { 406 | out = New(in.Shape...) 407 | } else if !EqualShape(out, in) { 408 | panic("Log1p:narrays must have equal shape.") 409 | } 410 | for k, v := range in.Data { 411 | out.Data[k] = float64(math.Log1p(float64(v))) 412 | } 413 | return out 414 | } 415 | 416 | // Logb applies math.Logb() elementwise to a multidimensional array. 417 | // See math package in standard lib for details. 418 | // 419 | // If 'out' is nil a new array is created. 420 | // Will panic if 'out' and 'in' shapes don't match. 421 | func Logb(out, in *NArray) *NArray { 422 | if out == nil { 423 | out = New(in.Shape...) 424 | } else if !EqualShape(out, in) { 425 | panic("Logb:narrays must have equal shape.") 426 | } 427 | for k, v := range in.Data { 428 | out.Data[k] = float64(math.Logb(float64(v))) 429 | } 430 | return out 431 | } 432 | 433 | // Cos applies math.Cos() elementwise to a multidimensional array. 434 | // See math package in standard lib for details. 435 | // 436 | // If 'out' is nil a new array is created. 437 | // Will panic if 'out' and 'in' shapes don't match. 438 | func Cos(out, in *NArray) *NArray { 439 | if out == nil { 440 | out = New(in.Shape...) 441 | } else if !EqualShape(out, in) { 442 | panic("Cos:narrays must have equal shape.") 443 | } 444 | for k, v := range in.Data { 445 | out.Data[k] = float64(math.Cos(float64(v))) 446 | } 447 | return out 448 | } 449 | 450 | // Sin applies math.Sin() elementwise to a multidimensional array. 451 | // See math package in standard lib for details. 452 | // 453 | // If 'out' is nil a new array is created. 454 | // Will panic if 'out' and 'in' shapes don't match. 455 | func Sin(out, in *NArray) *NArray { 456 | if out == nil { 457 | out = New(in.Shape...) 458 | } else if !EqualShape(out, in) { 459 | panic("Sin:narrays must have equal shape.") 460 | } 461 | for k, v := range in.Data { 462 | out.Data[k] = float64(math.Sin(float64(v))) 463 | } 464 | return out 465 | } 466 | 467 | // Sinh applies math.Sinh() elementwise to a multidimensional array. 468 | // See math package in standard lib for details. 469 | // 470 | // If 'out' is nil a new array is created. 471 | // Will panic if 'out' and 'in' shapes don't match. 472 | func Sinh(out, in *NArray) *NArray { 473 | if out == nil { 474 | out = New(in.Shape...) 475 | } else if !EqualShape(out, in) { 476 | panic("Sinh:narrays must have equal shape.") 477 | } 478 | for k, v := range in.Data { 479 | out.Data[k] = float64(math.Sinh(float64(v))) 480 | } 481 | return out 482 | } 483 | 484 | // Cosh applies math.Cosh() elementwise to a multidimensional array. 485 | // See math package in standard lib for details. 486 | // 487 | // If 'out' is nil a new array is created. 488 | // Will panic if 'out' and 'in' shapes don't match. 489 | func Cosh(out, in *NArray) *NArray { 490 | if out == nil { 491 | out = New(in.Shape...) 492 | } else if !EqualShape(out, in) { 493 | panic("Cosh:narrays must have equal shape.") 494 | } 495 | for k, v := range in.Data { 496 | out.Data[k] = float64(math.Cosh(float64(v))) 497 | } 498 | return out 499 | } 500 | 501 | // Tan applies math.Tan() elementwise to a multidimensional array. 502 | // See math package in standard lib for details. 503 | // 504 | // If 'out' is nil a new array is created. 505 | // Will panic if 'out' and 'in' shapes don't match. 506 | func Tan(out, in *NArray) *NArray { 507 | if out == nil { 508 | out = New(in.Shape...) 509 | } else if !EqualShape(out, in) { 510 | panic("Tan:narrays must have equal shape.") 511 | } 512 | for k, v := range in.Data { 513 | out.Data[k] = float64(math.Tan(float64(v))) 514 | } 515 | return out 516 | } 517 | 518 | // Tanh applies math.Tanh() elementwise to a multidimensional array. 519 | // See math package in standard lib for details. 520 | // 521 | // If 'out' is nil a new array is created. 522 | // Will panic if 'out' and 'in' shapes don't match. 523 | func Tanh(out, in *NArray) *NArray { 524 | if out == nil { 525 | out = New(in.Shape...) 526 | } else if !EqualShape(out, in) { 527 | panic("Tanh:narrays must have equal shape.") 528 | } 529 | for k, v := range in.Data { 530 | out.Data[k] = float64(math.Tanh(float64(v))) 531 | } 532 | return out 533 | } 534 | 535 | // Atan2 applies math.Atan2() elementwise to two multidimensional arrays. 536 | // See math package in standard lib for details. 537 | // 538 | // If out is nil a new array is created. 539 | // Will panic if 'out', 'a' and 'b' shapes don't match. 540 | func Atan2(out, a, b *NArray) *NArray { 541 | if out == nil { 542 | out = New(a.Shape...) 543 | } 544 | if !EqualShape(out, a, b) { 545 | panic("Atan2:narrays must have equal shape.") 546 | } 547 | for k, v := range a.Data { 548 | out.Data[k] = float64(math.Atan2(float64(v), float64(b.Data[k]))) 549 | } 550 | return out 551 | } 552 | 553 | // Dim applies math.Dim() elementwise to two multidimensional arrays. 554 | // See math package in standard lib for details. 555 | // 556 | // If out is nil a new array is created. 557 | // Will panic if 'out', 'a' and 'b' shapes don't match. 558 | func Dim(out, a, b *NArray) *NArray { 559 | if out == nil { 560 | out = New(a.Shape...) 561 | } 562 | if !EqualShape(out, a, b) { 563 | panic("Dim:narrays must have equal shape.") 564 | } 565 | for k, v := range a.Data { 566 | out.Data[k] = float64(math.Dim(float64(v), float64(b.Data[k]))) 567 | } 568 | return out 569 | } 570 | 571 | // Hypot applies math.Hypot() elementwise to two multidimensional arrays. 572 | // See math package in standard lib for details. 573 | // 574 | // If out is nil a new array is created. 575 | // Will panic if 'out', 'a' and 'b' shapes don't match. 576 | func Hypot(out, a, b *NArray) *NArray { 577 | if out == nil { 578 | out = New(a.Shape...) 579 | } 580 | if !EqualShape(out, a, b) { 581 | panic("Hypot:narrays must have equal shape.") 582 | } 583 | for k, v := range a.Data { 584 | out.Data[k] = float64(math.Hypot(float64(v), float64(b.Data[k]))) 585 | } 586 | return out 587 | } 588 | 589 | // Mod applies math.Mod() elementwise to two multidimensional arrays. 590 | // See math package in standard lib for details. 591 | // 592 | // If out is nil a new array is created. 593 | // Will panic if 'out', 'a' and 'b' shapes don't match. 594 | func Mod(out, a, b *NArray) *NArray { 595 | if out == nil { 596 | out = New(a.Shape...) 597 | } 598 | if !EqualShape(out, a, b) { 599 | panic("Mod:narrays must have equal shape.") 600 | } 601 | for k, v := range a.Data { 602 | out.Data[k] = float64(math.Mod(float64(v), float64(b.Data[k]))) 603 | } 604 | return out 605 | } 606 | 607 | // Pow applies math.Pow() elementwise to two multidimensional arrays. 608 | // See math package in standard lib for details. 609 | // 610 | // If out is nil a new array is created. 611 | // Will panic if 'out', 'a' and 'b' shapes don't match. 612 | func Pow(out, a, b *NArray) *NArray { 613 | if out == nil { 614 | out = New(a.Shape...) 615 | } 616 | if !EqualShape(out, a, b) { 617 | panic("Pow:narrays must have equal shape.") 618 | } 619 | for k, v := range a.Data { 620 | out.Data[k] = float64(math.Pow(float64(v), float64(b.Data[k]))) 621 | } 622 | return out 623 | } 624 | 625 | // Remainder applies math.Remainder() elementwise to two multidimensional arrays. 626 | // See math package in standard lib for details. 627 | // 628 | // If out is nil a new array is created. 629 | // Will panic if 'out', 'a' and 'b' shapes don't match. 630 | func Remainder(out, a, b *NArray) *NArray { 631 | if out == nil { 632 | out = New(a.Shape...) 633 | } 634 | if !EqualShape(out, a, b) { 635 | panic("Remainder:narrays must have equal shape.") 636 | } 637 | for k, v := range a.Data { 638 | out.Data[k] = float64(math.Remainder(float64(v), float64(b.Data[k]))) 639 | } 640 | return out 641 | } 642 | -------------------------------------------------------------------------------- /na64/narray.go: -------------------------------------------------------------------------------- 1 | // generated by narray; DO NOT EDIT 2 | 3 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 4 | // 5 | // Use of this source code is governed by a BSD-style 6 | // license that can be found in the LICENSE file. 7 | 8 | /* 9 | Package narray provides functions to opearate with multidimensional arrays of type float64. The NArray object is a dense, fixed-size, array of rank n. 10 | 11 | The shape is a vector with the size of each dimension. Typical cases: 12 | 13 | type rank example 14 | -------------------------------- 15 | scalar 0 na := New() 16 | vector 1 na := New(12) 17 | matrix 2 na := New(5,17) 18 | cube 3 na := New(2,3,5) 19 | 20 | */ 21 | package na64 22 | 23 | import ( 24 | "bytes" 25 | "encoding/json" 26 | "fmt" 27 | "io" 28 | "math" 29 | "math/rand" 30 | "os" 31 | "path/filepath" 32 | "strconv" 33 | ) 34 | 35 | // The NArray object. 36 | type NArray struct { 37 | // The rank or order or degree of the narray is the dimensionality required to represent it. (eg. The rank of a vector is 1) 38 | Rank int `json:"rank"` 39 | // The shape is an int slice that contains the size of each dimension. Subscripts range from zero to s-1. Where s is the size of a dimension. 40 | Shape []int `json:"shape"` 41 | // The data is stored as a slice of float64 numbers. 42 | Data []float64 `json:"data"` 43 | // Strides for each dimension. 44 | Strides []int `json:"strides"` 45 | } 46 | 47 | // New creates a new n-dimensional array. 48 | func New(shape ...int) *NArray { 49 | 50 | size := 1 51 | rank := len(shape) 52 | for _, v := range shape { 53 | size *= v 54 | } 55 | strides := make([]int, rank, rank) 56 | s := 1 57 | for i := (rank - 1); i >= 0; i-- { 58 | strides[i] = s 59 | s *= shape[i] 60 | } 61 | 62 | return &NArray{ 63 | Rank: rank, 64 | Shape: shape, 65 | Data: make([]float64, size, size), 66 | Strides: strides, 67 | } 68 | } 69 | 70 | // NewArray creates a new n-dimensional array with content of a slice 71 | // The size of the slice must match the product of the slice, 72 | // otherwise a panic will occur 73 | func NewArray(a []float64, shape ...int) *NArray { 74 | size := 1 75 | rank := len(shape) 76 | for _, v := range shape { 77 | size *= v 78 | } 79 | strides := make([]int, rank, rank) 80 | s := 1 81 | for i := (rank - 1); i >= 0; i-- { 82 | strides[i] = s 83 | s *= shape[i] 84 | } 85 | 86 | if len(a) != size { 87 | panic("slice doesn't match size") 88 | } 89 | return &NArray{ 90 | Rank: rank, 91 | Shape: shape, 92 | Data: a, 93 | Strides: strides, 94 | } 95 | } 96 | 97 | // Norm creates a new n-dimensional array whose 98 | // elements are drawn from a Normal probability density function. 99 | func Norm(r *rand.Rand, mean, sd float64, shape ...int) *NArray { 100 | 101 | na := New(shape...) 102 | for i := range na.Data { 103 | na.Data[i] = float64(r.NormFloat64())*sd + mean 104 | } 105 | return na 106 | } 107 | 108 | // Rand creates a new n-dimensional array whose 109 | // elements are set using the rand.Float64 function. 110 | // Values are pseudo-random numbers in [0.0,1.0). 111 | func Rand(r *rand.Rand, shape ...int) *NArray { 112 | 113 | na := New(shape...) 114 | for i := range na.Data { 115 | na.Data[i] = r.Float64() 116 | } 117 | return na 118 | } 119 | 120 | // At returns the value for indices. 121 | func (na *NArray) At(indices ...int) float64 { 122 | 123 | if len(indices) != na.Rank { 124 | fmt.Errorf("inconsistent number of indices for narray - [%d] vs [%d]", len(indices), na.Rank) 125 | } 126 | 127 | // return na.Data[na.Index(indices...)] 128 | return na.Data[na.Index(indices...)] 129 | } 130 | 131 | // Set value for indices. 132 | func (na *NArray) Set(v float64, indices ...int) { 133 | 134 | na.Data[na.Index(indices...)] = v 135 | } 136 | 137 | // Index transforms a set of subscripts to a an index in the underlying one-dimensional slice. 138 | func (na *NArray) Index(indices ...int) int { 139 | 140 | idx := 0 141 | for k, v := range indices { 142 | idx += v * na.Strides[k] 143 | } 144 | return idx 145 | } 146 | 147 | // ReverseIndex converts a linear index to narray indices. 148 | func (na *NArray) ReverseIndex(idx int) []int { 149 | 150 | res := make([]int, na.Rank, na.Rank) 151 | temp := idx 152 | p := 1 153 | for k := 1; k < na.Rank; k++ { 154 | p *= na.Shape[k] 155 | } 156 | for i := 0; i < na.Rank; i++ { 157 | res[i] = temp / p 158 | temp = temp % p 159 | if (i + 1) < na.Rank { 160 | p /= na.Shape[i+1] 161 | } 162 | } 163 | return res 164 | } 165 | 166 | // Copy returns a copy on the narray. 167 | func (na *NArray) Copy() *NArray { 168 | 169 | newna := New(na.Shape...) 170 | copy(newna.Data, na.Data) 171 | return newna 172 | } 173 | 174 | // ApplyFunc is a type for creating custom functions. 175 | type ApplyFunc func(x float64) float64 176 | 177 | // Apply function of type ApplyFunc to a multidimensional array. 178 | // If out is nil, a new object is allocated. 179 | func Apply(out, in *NArray, fn ApplyFunc) *NArray { 180 | 181 | if out == nil { 182 | out = New(in.Shape...) 183 | } 184 | for i := 0; i < len(in.Data); i++ { 185 | out.Data[i] = fn(in.Data[i]) 186 | } 187 | return out 188 | } 189 | 190 | // EqualShape returns true if all the arrays have equal length, 191 | // and false otherwise. Returns true if there is only one input array. 192 | func EqualShape(x *NArray, ys ...*NArray) bool { 193 | shape := x.Shape 194 | l := len(shape) 195 | for _, y := range ys { 196 | if len(y.Shape) != l { 197 | return false 198 | } 199 | for j, d := range shape { 200 | if y.Shape[j] != d { 201 | return false 202 | } 203 | } 204 | } 205 | return true 206 | } 207 | 208 | // Inc increments the value of an narray element. 209 | func (na *NArray) Inc(v float64, indices ...int) { 210 | 211 | na.Data[na.Index(indices...)] += v 212 | } 213 | 214 | // MaxElem compares value to element and replaces element if 215 | // value is greater than element. 216 | func (na *NArray) MaxElem(v float64, indices ...int) { 217 | 218 | idx := na.Index(indices...) 219 | if v > na.Data[idx] { 220 | na.Data[idx] = v 221 | } 222 | } 223 | 224 | // MinElem compares value to element and replaces element if 225 | // value is less than element. 226 | func (na *NArray) MinElem(v float64, indices ...int) { 227 | 228 | idx := na.Index(indices...) 229 | if v < na.Data[idx] { 230 | na.Data[idx] = v 231 | } 232 | } 233 | 234 | // Add adds narrays elementwise. 235 | // out = sum_i(in[i]) 236 | // Will panic if there are not at least two input narrays 237 | // or if narray shapes don't match. 238 | // If out is nil a new array is created. 239 | func Add(out *NArray, in ...*NArray) *NArray { 240 | 241 | if len(in) < 2 { 242 | return nil 243 | } 244 | if out == nil { 245 | out = New(in[0].Shape...) 246 | } 247 | if !EqualShape(out, in...) { 248 | panic("narrays must have equal shape.") 249 | } 250 | 251 | addSlice(out.Data, in[0].Data, in[1].Data) 252 | 253 | // Multiply each following, if more than two arguments. 254 | for k := 2; k < len(in); k++ { 255 | addSlice(out.Data, out.Data, in[k].Data) 256 | } 257 | 258 | return out 259 | } 260 | 261 | // Mul multiplies narrays elementwise. 262 | // out = prod_i(in[i]) 263 | // Will panic if there are not at least two input narrays 264 | // or if narray shapes don't match. 265 | // If out is nil a new array is created. 266 | func Mul(out *NArray, in ...*NArray) *NArray { 267 | 268 | if len(in) < 2 { 269 | panic("not in enough arguments") 270 | } 271 | if out == nil { 272 | out = New(in[0].Shape...) 273 | } 274 | if !EqualShape(out, in...) { 275 | panic("narrays must have equal shape.") 276 | } 277 | 278 | mulSlice(out.Data, in[0].Data, in[1].Data) 279 | 280 | // Multiply each following, if more than two arguments. 281 | for k := 2; k < len(in); k++ { 282 | mulSlice(out.Data, out.Data, in[k].Data) 283 | } 284 | return out 285 | } 286 | 287 | // Dot computes the sum of the elementwise products of 288 | // the input arrays. 289 | // 290 | // y = sum_{i = 0}^(N-1) x0[i]*x1[i]*...x_n-1[i] 291 | // 292 | // Will panic if there are not at least two input narrays 293 | // or if narray shapes don't match. 294 | func Dot(in ...*NArray) float64 { 295 | 296 | if len(in) < 2 { 297 | panic("not in enough arguments") 298 | } 299 | if !EqualShape(in[0], in...) { 300 | panic("narrays must have equal shape.") 301 | } 302 | n := len(in[0].Data) 303 | out := make([]float64, n, n) 304 | mulSlice(out, in[0].Data, in[1].Data) 305 | 306 | // Multiply each following, if more than two arguments. 307 | for k := 2; k < len(in); k++ { 308 | mulSlice(out, out, in[k].Data) 309 | } 310 | return sliceSum(out) 311 | } 312 | 313 | // Div divides narrays elementwise. 314 | // out = in[0] / in[1] / in[2] .... 315 | // Will panic if there are not at least two input narrays 316 | // or if narray shapes don't match. 317 | // If out is nil a new array is created. 318 | func Div(out *NArray, in ...*NArray) *NArray { 319 | 320 | if len(in) < 2 { 321 | panic("not in enough arguments") 322 | } 323 | if out == nil { 324 | out = New(in[0].Shape...) 325 | } 326 | if !EqualShape(out, in...) { 327 | panic("narrays must have equal shape.") 328 | } 329 | 330 | divSlice(out.Data, in[0].Data, in[1].Data) 331 | 332 | // Multiply each following, if more than two arguments. 333 | for k := 2; k < len(in); k++ { 334 | divSlice(out.Data, out.Data, in[k].Data) 335 | } 336 | return out 337 | } 338 | 339 | // Sub subtracts narrays elementwise. 340 | // out = in[0] - in[1] - in[2] .... 341 | // Will panic if there are not at least two input narrays 342 | // or if narray shapes don't match. 343 | // If out is nil a new array is created. 344 | func Sub(out *NArray, in ...*NArray) *NArray { 345 | 346 | if len(in) < 2 { 347 | panic("not in enough arguments") 348 | } 349 | if out == nil { 350 | out = New(in[0].Shape...) 351 | } 352 | if !EqualShape(out, in...) { 353 | panic("narrays must have equal shape.") 354 | } 355 | 356 | subSlice(out.Data, in[0].Data, in[1].Data) 357 | 358 | // Multiply each following, if more than two arguments. 359 | for k := 2; k < len(in); k++ { 360 | subSlice(out.Data, out.Data, in[k].Data) 361 | } 362 | return out 363 | } 364 | 365 | // AddConst adds const to an narray elementwise. 366 | // out = in + c 367 | // If out is nil a new array is created. 368 | func AddConst(out *NArray, in *NArray, c float64) *NArray { 369 | 370 | if out == nil { 371 | out = New(in.Shape...) 372 | } else { 373 | if !EqualShape(out, in) { 374 | panic("narrays must have equal shape.") 375 | } 376 | } 377 | caddSlice(out.Data, in.Data, c) 378 | return out 379 | } 380 | 381 | // AddScaled adds a scaled narray elementwise. 382 | // y = y + a * x 383 | // If y is nil a new array is created. 384 | func AddScaled(y *NArray, x *NArray, a float64) *NArray { 385 | 386 | if y == nil { 387 | y = New(x.Shape...) 388 | } else { 389 | if !EqualShape(y, x) { 390 | panic("narrays must have equal shape.") 391 | } 392 | } 393 | addScaledSlice(y.Data, x.Data, a) 394 | return y 395 | } 396 | 397 | // Scale multiplies an narray by a factor elementwise. 398 | // out = c * in 399 | // If out is nil a new array is created. 400 | func Scale(out *NArray, in *NArray, c float64) *NArray { 401 | 402 | if out == nil { 403 | out = New(in.Shape...) 404 | } else { 405 | if !EqualShape(out, in) { 406 | panic("narrays must have equal shape.") 407 | } 408 | } 409 | cmulSlice(out.Data, in.Data, c) 410 | return out 411 | } 412 | 413 | // Rcp returns reciprocal values of narrays elementwise. 414 | // out = 1.0 / in 415 | // If out is nil a new array is created. 416 | func Rcp(out, in *NArray) *NArray { 417 | if out == nil { 418 | out = New(in.Shape...) 419 | } else { 420 | if !EqualShape(out, in) { 421 | panic("narrays must have equal shape.") 422 | } 423 | } 424 | cdivSlice(out.Data, in.Data, 1.0) 425 | return out 426 | } 427 | 428 | // Sqrt returns square root values of narrays elementwise. 429 | // out = math.Sqrt(in) 430 | // If out is nil a new array is created. 431 | func Sqrt(out, in *NArray) *NArray { 432 | if out == nil { 433 | out = New(in.Shape...) 434 | } else { 435 | if !EqualShape(out, in) { 436 | panic("narrays must have equal shape.") 437 | } 438 | } 439 | sqrtSlice(out.Data, in.Data) 440 | return out 441 | } 442 | 443 | // Abs returns square root values of narrays elementwise. 444 | // out = math.Abs(in) 445 | // If out is nil a new array is created. 446 | func Abs(out, in *NArray) *NArray { 447 | if out == nil { 448 | out = New(in.Shape...) 449 | } else { 450 | if !EqualShape(out, in) { 451 | panic("narrays must have equal shape.") 452 | } 453 | } 454 | absSlice(out.Data, in.Data) 455 | return out 456 | } 457 | 458 | // Max returns the max value in the narray. 459 | func (na *NArray) Max() float64 { 460 | if na == nil || len(na.Data) == 0 { 461 | panic("unable to take max of nil or zero-sizes array") 462 | } 463 | return maxSliceElement(na.Data) 464 | } 465 | 466 | // MaxIdx returns the max value and corresponding indices. 467 | func (na *NArray) MaxIdx() (float64, []int) { 468 | 469 | var offset int 470 | max := -math.MaxFloat64 471 | for i := 0; i < len(na.Data); i++ { 472 | if na.Data[i] > max { 473 | max = na.Data[i] 474 | offset = i 475 | } 476 | } 477 | return max, na.ReverseIndex(offset) 478 | } 479 | 480 | // MaxArray compare input narrays and returns an narray containing 481 | // the element-wise maxima. 482 | // out[i,j,k,...] = max(in0[i,j,k,...], in1[i,j,k,...], ...) 483 | // Will panic if there are not at least two input narray 484 | // or if narray shapes don't match. 485 | // If out is nil a new array is created. 486 | func MaxArray(out *NArray, in ...*NArray) *NArray { 487 | 488 | if len(in) < 2 { 489 | panic("not in enough input narrays") 490 | } 491 | if out == nil { 492 | out = New(in[0].Shape...) 493 | } 494 | if !EqualShape(out, in...) { 495 | panic("narrays must have equal shape.") 496 | } 497 | 498 | maxSlice(out.Data, in[0].Data, in[1].Data) 499 | 500 | // Also add each following, if more than two arguments. 501 | for k := 2; k < len(in); k++ { 502 | maxSlice(out.Data, out.Data, in[k].Data) 503 | } 504 | return out 505 | } 506 | 507 | // Copysign returns values with the magnitude of a and the sign of b 508 | // for each element of the arrays. 509 | // Will panic if narray shapes don't match. 510 | // If out is nil a new array is created. 511 | func Copysign(out, a, b *NArray) *NArray { 512 | 513 | if out == nil { 514 | out = New(a.Shape...) 515 | } 516 | if !EqualShape(out, a, b) { 517 | panic("narrays must have equal shape.") 518 | } 519 | 520 | csignSlice(out.Data, a.Data, b.Data) 521 | 522 | return out 523 | } 524 | 525 | // Min returns the min value in the narray. 526 | func (na *NArray) Min() float64 { 527 | if na == nil || len(na.Data) == 0 { 528 | panic("unable to take min of nil or zero-sizes array") 529 | } 530 | return minSliceElement(na.Data) 531 | } 532 | 533 | // MinIdx returns the min value and corresponding indices. 534 | func (na *NArray) MinIdx() (float64, []int) { 535 | 536 | var offset int 537 | min := math.MaxFloat64 538 | for i := 0; i < len(na.Data); i++ { 539 | if na.Data[i] < min { 540 | min = na.Data[i] 541 | offset = i 542 | } 543 | } 544 | return min, na.ReverseIndex(offset) 545 | } 546 | 547 | // MinArray compare input narrays and returns an narray containing 548 | // the element-wise minima. 549 | // out[i,j,k,...] = min(in0[i,j,k,...], in1[i,j,k,...], ...) 550 | // Will panic if there are not at least two input narray 551 | // or if narray shapes don't match. 552 | // If out is nil a new array is created. 553 | func MinArray(out *NArray, in ...*NArray) *NArray { 554 | 555 | if len(in) < 2 { 556 | panic("not in enough input narrays") 557 | } 558 | if out == nil { 559 | out = New(in[0].Shape...) 560 | } 561 | if !EqualShape(out, in...) { 562 | panic("narrays must have equal shape.") 563 | } 564 | 565 | minSlice(out.Data, in[0].Data, in[1].Data) 566 | 567 | // Also add each following, if more than two arguments. 568 | for k := 2; k < len(in); k++ { 569 | minSlice(out.Data, out.Data, in[k].Data) 570 | } 571 | 572 | return out 573 | } 574 | 575 | // Prod returns the products of all the elements in the narray. 576 | func (na *NArray) Prod() float64 { 577 | 578 | p := float64(1.0) 579 | for _, v := range na.Data { 580 | p *= v 581 | } 582 | return p 583 | } 584 | 585 | // Sum returns the sum of all the elements in the narray. 586 | func (na *NArray) Sum() float64 { 587 | return sliceSum(na.Data) 588 | } 589 | 590 | // SetValue sets all elements to value. 591 | func (na *NArray) SetValue(v float64) *NArray { 592 | 593 | for i := range na.Data { 594 | na.Data[i] = v 595 | } 596 | return na 597 | } 598 | 599 | // Encode converts values in-place as follows: 600 | // Inf to math.MaxFloat64 601 | // -Inf to -math.MaxFloat64 602 | // NaN ro 0 603 | // 604 | // Returns the indices of the modified values as follows: 605 | // Values in inf => na.Data[abs(v)] = sign(v) * Inf 606 | // Values in nan => na.Data[v] = NaN 607 | func (na *NArray) Encode() (inf, nan []int) { 608 | 609 | inf = []int{} 610 | nan = []int{} 611 | for k, v := range na.Data { 612 | switch { 613 | case math.IsInf(float64(v), 1): 614 | na.Data[k] = math.MaxFloat64 615 | inf = append(inf, k) 616 | case math.IsInf(float64(v), -1): 617 | na.Data[k] = -math.MaxFloat64 618 | inf = append(inf, -k) 619 | case math.IsNaN(float64(v)): 620 | na.Data[k] = 0 621 | nan = append(nan, k) 622 | } 623 | } 624 | return 625 | } 626 | 627 | // Decode converts values in-place. 628 | // See Encode() for details. 629 | func (na *NArray) Decode(inf, nan []int) { 630 | pInf := float64(math.Inf(1)) 631 | nInf := float64(math.Inf(-1)) 632 | fNan := float64(math.NaN()) 633 | 634 | for _, v := range inf { 635 | if v >= 0 { 636 | na.Data[v] = pInf 637 | } else { 638 | na.Data[-v] = nInf 639 | } 640 | } 641 | for _, v := range nan { 642 | na.Data[v] = fNan 643 | } 644 | } 645 | 646 | // SubArray returns an narray of lower rank as follows: 647 | // 648 | // Example, given an narray with shape 2x3x4 (rank=3), return the subarray 649 | // of rank=2 corresponding to dim[2]=1 650 | // 651 | // x := New(2,3,4) 652 | // y := x.SubArray(-1,-1,1) // use -1 to select a dimension. Put a 1 in dim=2 (third argument). 653 | // // y = {x(0,0,1), x(0,1,1), x(0,2,1), x(1,0,1), ...} 654 | // 655 | func (na *NArray) SubArray(query ...int) *NArray { 656 | 657 | if len(na.Shape) == 0 { 658 | panic("cannot get subarray from narray with rank=0") 659 | } 660 | 661 | qs := querySubset(query, na.Shape) 662 | var ns []int // new shape 663 | for k, v := range query { 664 | if v < 0 { 665 | ns = append(ns, na.Shape[k]) 666 | } 667 | } 668 | newArr := New(ns...) 669 | for k, v := range qs { 670 | newArr.Data[k] = na.At(v...) 671 | } 672 | 673 | return newArr 674 | } 675 | 676 | // Reshape returns an narray with a new shape. 677 | func (na *NArray) Reshape(dim ...int) *NArray { 678 | panic("not implemented") 679 | } 680 | 681 | // Sprint prints narray elements when f returns true. 682 | // index is the linear index of an narray. 683 | func (na *NArray) Sprint(f func(na *NArray, index int) bool) string { 684 | 685 | b := bytes.NewBufferString(fmt.Sprintln("narray rank: ", na.Rank)) 686 | _, _ = b.WriteString(fmt.Sprintln("narray shape: ", na.Shape)) 687 | for k, v := range na.Data { 688 | idx := na.ReverseIndex(k) 689 | if f(na, k) { 690 | _, _ = b.WriteString("[") 691 | for axis, av := range idx { 692 | _, _ = b.WriteString(formatted(av, na.Shape[axis]-1)) 693 | } 694 | _, _ = b.WriteString(fmt.Sprintf("] => %f\n", v)) 695 | } 696 | } 697 | return b.String() 698 | } 699 | 700 | // Read unmarshals json data from an io.Reader into an narray struct. 701 | func Read(r io.Reader) (*NArray, error) { 702 | dec := json.NewDecoder(r) 703 | var na NArray 704 | err := dec.Decode(&na) 705 | if err != nil && err != io.EOF { 706 | return nil, err 707 | } 708 | return &na, nil 709 | } 710 | 711 | // ReadFile unmarshals json data from a file into an narray struct. 712 | func ReadFile(fn string) (*NArray, error) { 713 | 714 | f, err := os.Open(fn) 715 | if err != nil { 716 | return nil, err 717 | } 718 | defer f.Close() 719 | return Read(f) 720 | } 721 | 722 | // Write writes narray to an io.Writer. 723 | func (na *NArray) Write(w io.Writer) error { 724 | 725 | enc := json.NewEncoder(w) 726 | err := enc.Encode(na) 727 | if err != nil { 728 | return err 729 | } 730 | return nil 731 | } 732 | 733 | // WriteFile writes an narray to a file. 734 | func (na *NArray) WriteFile(fn string) error { 735 | 736 | e := os.MkdirAll(filepath.Dir(fn), 0755) 737 | if e != nil { 738 | return e 739 | } 740 | f, err := os.Create(fn) 741 | if err != nil { 742 | return err 743 | } 744 | defer f.Close() 745 | 746 | ee := na.Write(f) 747 | if ee != nil { 748 | return ee 749 | } 750 | return nil 751 | } 752 | 753 | // ToJSON returns a json string. 754 | func (na *NArray) ToJSON() (string, error) { 755 | var b bytes.Buffer 756 | err := na.Write(&b) 757 | return b.String(), err 758 | } 759 | 760 | // MarshalJSON implements the json.Marshaller interface. 761 | // The custom marshaller is needed to encode Inf/NaN values. 762 | func (na *NArray) MarshalJSON() ([]byte, error) { 763 | 764 | ena := na.Copy() 765 | inf, nan := ena.Encode() 766 | return json.Marshal(struct { 767 | Rank int `json:"rank"` 768 | Shape []int `json:"shape"` 769 | Data []float64 `json:"data"` 770 | Strides []int `json:"strides"` 771 | Inf []int `json:"inf,omitempty"` 772 | NaN []int `json:"nan,omitempty"` 773 | }{ 774 | Rank: ena.Rank, 775 | Shape: ena.Shape, 776 | Data: ena.Data, 777 | Strides: ena.Strides, 778 | Inf: inf, 779 | NaN: nan, 780 | }) 781 | } 782 | 783 | // UnmarshalJSON implements the json.Unarshaller interface. 784 | // The custom unmarshaller is needed to decode Inf/NaN values. 785 | func (na *NArray) UnmarshalJSON(b []byte) error { 786 | x := struct { 787 | Rank int `json:"rank"` 788 | Shape []int `json:"shape"` 789 | Data []float64 `json:"data"` 790 | Strides []int `json:"strides"` 791 | Inf []int `json:"inf,omitempty"` 792 | NaN []int `json:"nan,omitempty"` 793 | }{} 794 | 795 | err := json.Unmarshal(b, &x) 796 | if err != nil { 797 | return err 798 | } 799 | 800 | na.Rank = x.Rank 801 | na.Shape = x.Shape 802 | na.Data = x.Data 803 | na.Strides = x.Strides 804 | na.Decode(x.Inf, x.NaN) 805 | return nil 806 | } 807 | 808 | // String prints the narray 809 | func (na *NArray) String() string { 810 | 811 | return na.Sprint(func(na *NArray, k int) bool { 812 | return true 813 | }) 814 | } 815 | 816 | // equal returns true if |x-y|/(|avg(x,y)|+1) < tol. 817 | func equal(x, y float64, tol float64) bool { 818 | avg := (math.Abs(float64(x+y)) / 2.0) 819 | sErr := math.Abs(float64(x-y)) / (avg + 1) 820 | if sErr > tol { 821 | return false 822 | } 823 | return true 824 | } 825 | 826 | // EqualValues compares two narrays elementwise. 827 | // Returns true if for all elements |x-y|/(|avg(x,y)|+1) < tol. 828 | func EqualValues(x *NArray, y *NArray, tol float64) bool { 829 | if !EqualShape(x, y) { 830 | panic("narrays must have equal shape.") 831 | } 832 | for i, _ := range x.Data { 833 | if !equal(x.Data[i], y.Data[i], tol) { 834 | return false 835 | } 836 | } 837 | return true 838 | } 839 | 840 | func formatted(n, max int) string { 841 | b := bytes.NewBufferString(" ") 842 | for i := 0; i < nd(max)-nd(n); i++ { 843 | _, _ = b.WriteString(" ") 844 | } 845 | _, _ = b.WriteString(strconv.FormatInt(int64(n), 10)) 846 | return b.String() 847 | } 848 | 849 | // num digits in number 850 | func nd(n int) int { 851 | if n == 0 { 852 | return 1 853 | } 854 | return int(math.Log10(float64(n))) + 1 855 | } 856 | 857 | func cartesianProduct(s []int) [][]int { 858 | 859 | if len(s) == 1 { 860 | z := make([][]int, s[0], s[0]) 861 | for k := range z { 862 | z[k] = []int{k} 863 | } 864 | return z 865 | } 866 | var result [][]int 867 | for i := 0; i < s[0]; i++ { 868 | x := cartesianProduct(s[1:]) 869 | for _, v := range x { 870 | var sl []int 871 | sl = append(sl, i) 872 | sl = append(sl, v...) 873 | result = append(result, sl) 874 | } 875 | } 876 | return result 877 | } 878 | 879 | // Recursively find indices for query q. 880 | // Helper func to generate narray subsets. 881 | func querySubset(q, s []int) [][]int { 882 | 883 | if len(q) != len(s) { 884 | panic("size mismatch") 885 | } 886 | var result [][]int 887 | 888 | switch { 889 | case len(s) == 1 && q[0] >= 0: 890 | result = [][]int{[]int{q[0]}} 891 | 892 | case len(s) == 1 && q[0] < 0: 893 | result = make([][]int, s[0], s[0]) 894 | for k := range result { 895 | result[k] = []int{k} 896 | } 897 | 898 | case q[0] >= 0: 899 | x := querySubset(q[1:], s[1:]) 900 | for _, v := range x { 901 | var sl []int 902 | sl = append(sl, q[0]) 903 | sl = append(sl, v...) 904 | result = append(result, sl) 905 | } 906 | 907 | case q[0] < 0: 908 | for i := 0; i < s[0]; i++ { 909 | x := querySubset(q[1:], s[1:]) 910 | for _, v := range x { 911 | var sl []int 912 | sl = append(sl, i) 913 | sl = append(sl, v...) 914 | result = append(result, sl) 915 | } 916 | } 917 | } 918 | return result 919 | } 920 | -------------------------------------------------------------------------------- /narray.go.tpl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 AKUALAB INC., All rights reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | /* 7 | Package narray provides functions to opearate with multidimensional arrays of type {{.Format}}. The NArray object is a dense, fixed-size, array of rank n. 8 | 9 | The shape is a vector with the size of each dimension. Typical cases: 10 | 11 | type rank example 12 | -------------------------------- 13 | scalar 0 na := New() 14 | vector 1 na := New(12) 15 | matrix 2 na := New(5,17) 16 | cube 3 na := New(2,3,5) 17 | 18 | */ 19 | package {{.Package}} 20 | 21 | import ( 22 | "bytes" 23 | "encoding/json" 24 | "fmt" 25 | "io" 26 | "math" 27 | "math/rand" 28 | "os" 29 | "path/filepath" 30 | "strconv" 31 | ) 32 | 33 | // The NArray object. 34 | type NArray struct { 35 | // The rank or order or degree of the narray is the dimensionality required to represent it. (eg. The rank of a vector is 1) 36 | Rank int `json:"rank"` 37 | // The shape is an int slice that contains the size of each dimension. Subscripts range from zero to s-1. Where s is the size of a dimension. 38 | Shape []int `json:"shape"` 39 | // The data is stored as a slice of {{.Format}} numbers. 40 | Data []{{.Format}} `json:"data"` 41 | // Strides for each dimension. 42 | Strides []int `json:"strides"` 43 | } 44 | 45 | // New creates a new n-dimensional array. 46 | func New(shape ...int) *NArray { 47 | 48 | size := 1 49 | rank := len(shape) 50 | for _, v := range shape { 51 | size *= v 52 | } 53 | strides := make([]int, rank, rank) 54 | s := 1 55 | for i := (rank - 1); i >= 0; i-- { 56 | strides[i] = s 57 | s *= shape[i] 58 | } 59 | 60 | return &NArray{ 61 | Rank: rank, 62 | Shape: shape, 63 | Data: make([]{{.Format}}, size, size), 64 | Strides: strides, 65 | } 66 | } 67 | 68 | // NewArray creates a new n-dimensional array with content of a slice 69 | // The size of the slice must match the product of the slice, 70 | // otherwise a panic will occur 71 | func NewArray(a []{{.Format}},shape ...int) *NArray { 72 | size := 1 73 | rank := len(shape) 74 | for _, v := range shape { 75 | size *= v 76 | } 77 | strides := make([]int, rank, rank) 78 | s := 1 79 | for i := (rank - 1); i >= 0; i-- { 80 | strides[i] = s 81 | s *= shape[i] 82 | } 83 | 84 | if len(a) != size { 85 | panic("slice doesn't match size") 86 | } 87 | return &NArray{ 88 | Rank: rank, 89 | Shape: shape, 90 | Data: a, 91 | Strides: strides, 92 | } 93 | } 94 | 95 | // Norm creates a new n-dimensional array whose 96 | // elements are drawn from a Normal probability density function. 97 | func Norm(r *rand.Rand, mean, sd {{.Format}}, shape ...int) *NArray { 98 | 99 | na := New(shape...) 100 | for i := range na.Data { 101 | na.Data[i] = {{.Format}}(r.NormFloat64())*sd + mean 102 | } 103 | return na 104 | } 105 | 106 | // Rand creates a new n-dimensional array whose 107 | // elements are set using the rand.Float64 function. 108 | // Values are pseudo-random numbers in [0.0,1.0). 109 | func Rand(r *rand.Rand, shape ...int) *NArray { 110 | 111 | na := New(shape...) 112 | for i := range na.Data { 113 | {{if .Float64}}na.Data[i] = r.Float64(){{end}} {{if .Float32}}na.Data[i] = r.Float32(){{end}} 114 | } 115 | return na 116 | } 117 | 118 | // At returns the value for indices. 119 | func (na *NArray) At(indices ...int) {{.Format}} { 120 | 121 | if len(indices) != na.Rank { 122 | fmt.Errorf("inconsistent number of indices for narray - [%d] vs [%d]", len(indices), na.Rank) 123 | } 124 | 125 | // return na.Data[na.Index(indices...)] 126 | return na.Data[na.Index(indices...)] 127 | } 128 | 129 | // Set value for indices. 130 | func (na *NArray) Set(v {{.Format}}, indices ...int) { 131 | 132 | na.Data[na.Index(indices...)] = v 133 | } 134 | 135 | // Index transforms a set of subscripts to a an index in the underlying one-dimensional slice. 136 | func (na *NArray) Index(indices ...int) int { 137 | 138 | idx := 0 139 | for k, v := range indices { 140 | idx += v * na.Strides[k] 141 | } 142 | return idx 143 | } 144 | 145 | // ReverseIndex converts a linear index to narray indices. 146 | func (na *NArray) ReverseIndex(idx int) []int { 147 | 148 | res := make([]int, na.Rank, na.Rank) 149 | temp := idx 150 | p := 1 151 | for k := 1; k < na.Rank; k++ { 152 | p *= na.Shape[k] 153 | } 154 | for i := 0; i < na.Rank; i++ { 155 | res[i] = temp / p 156 | temp = temp % p 157 | if (i + 1) < na.Rank { 158 | p /= na.Shape[i+1] 159 | } 160 | } 161 | return res 162 | } 163 | 164 | // Copy returns a copy on the narray. 165 | func (na *NArray) Copy() *NArray { 166 | 167 | newna := New(na.Shape...) 168 | copy(newna.Data, na.Data) 169 | return newna 170 | } 171 | 172 | // ApplyFunc is a type for creating custom functions. 173 | type ApplyFunc func(x {{.Format}}) {{.Format}} 174 | 175 | // Apply function of type ApplyFunc to a multidimensional array. 176 | // If out is nil, a new object is allocated. 177 | func Apply(out, in *NArray, fn ApplyFunc) *NArray { 178 | 179 | if out == nil { 180 | out = New(in.Shape...) 181 | } 182 | for i := 0; i < len(in.Data); i++ { 183 | out.Data[i] = fn(in.Data[i]) 184 | } 185 | return out 186 | } 187 | 188 | // EqualShape returns true if all the arrays have equal length, 189 | // and false otherwise. Returns true if there is only one input array. 190 | func EqualShape(x *NArray, ys ...*NArray) bool { 191 | shape := x.Shape 192 | l := len(shape) 193 | for _, y := range ys { 194 | if len(y.Shape) != l { 195 | return false 196 | } 197 | for j, d := range shape { 198 | if y.Shape[j] != d { 199 | return false 200 | } 201 | } 202 | } 203 | return true 204 | } 205 | 206 | // Inc increments the value of an narray element. 207 | func (na *NArray) Inc(v {{.Format}}, indices ...int) { 208 | 209 | na.Data[na.Index(indices...)] += v 210 | } 211 | 212 | // MaxElem compares value to element and replaces element if 213 | // value is greater than element. 214 | func (na *NArray) MaxElem(v {{.Format}}, indices ...int) { 215 | 216 | idx := na.Index(indices...) 217 | if v > na.Data[idx] { 218 | na.Data[idx] = v 219 | } 220 | } 221 | 222 | // MinElem compares value to element and replaces element if 223 | // value is less than element. 224 | func (na *NArray) MinElem(v {{.Format}}, indices ...int) { 225 | 226 | idx := na.Index(indices...) 227 | if v < na.Data[idx] { 228 | na.Data[idx] = v 229 | } 230 | } 231 | 232 | // Add adds narrays elementwise. 233 | // out = sum_i(in[i]) 234 | // Will panic if there are not at least two input narrays 235 | // or if narray shapes don't match. 236 | // If out is nil a new array is created. 237 | func Add(out *NArray, in ...*NArray) *NArray { 238 | 239 | if len(in) < 2 { 240 | return nil 241 | } 242 | if out == nil { 243 | out = New(in[0].Shape...) 244 | } 245 | if !EqualShape(out, in...) { 246 | panic("narrays must have equal shape.") 247 | } 248 | 249 | addSlice(out.Data, in[0].Data, in[1].Data) 250 | 251 | // Multiply each following, if more than two arguments. 252 | for k := 2; k < len(in); k++ { 253 | addSlice(out.Data, out.Data, in[k].Data) 254 | } 255 | 256 | return out 257 | } 258 | 259 | // Mul multiplies narrays elementwise. 260 | // out = prod_i(in[i]) 261 | // Will panic if there are not at least two input narrays 262 | // or if narray shapes don't match. 263 | // If out is nil a new array is created. 264 | func Mul(out *NArray, in ...*NArray) *NArray { 265 | 266 | if len(in) < 2 { 267 | panic("not in enough arguments") 268 | } 269 | if out == nil { 270 | out = New(in[0].Shape...) 271 | } 272 | if !EqualShape(out, in...) { 273 | panic("narrays must have equal shape.") 274 | } 275 | 276 | mulSlice(out.Data, in[0].Data, in[1].Data) 277 | 278 | // Multiply each following, if more than two arguments. 279 | for k := 2; k < len(in); k++ { 280 | mulSlice(out.Data, out.Data, in[k].Data) 281 | } 282 | return out 283 | } 284 | 285 | // Dot computes the sum of the elementwise products of 286 | // the input arrays. 287 | // 288 | // y = sum_{i = 0}^(N-1) x0[i]*x1[i]*...x_n-1[i] 289 | // 290 | // Will panic if there are not at least two input narrays 291 | // or if narray shapes don't match. 292 | func Dot(in ...*NArray) {{.Format}} { 293 | 294 | if len(in) < 2 { 295 | panic("not in enough arguments") 296 | } 297 | if !EqualShape(in[0], in...) { 298 | panic("narrays must have equal shape.") 299 | } 300 | n:=len(in[0].Data) 301 | out := make([]{{.Format}},n,n) 302 | mulSlice(out, in[0].Data, in[1].Data) 303 | 304 | // Multiply each following, if more than two arguments. 305 | for k := 2; k < len(in); k++ { 306 | mulSlice(out, out, in[k].Data) 307 | } 308 | return sliceSum(out) 309 | } 310 | 311 | // Div divides narrays elementwise. 312 | // out = in[0] / in[1] / in[2] .... 313 | // Will panic if there are not at least two input narrays 314 | // or if narray shapes don't match. 315 | // If out is nil a new array is created. 316 | func Div(out *NArray, in ...*NArray) *NArray { 317 | 318 | if len(in) < 2 { 319 | panic("not in enough arguments") 320 | } 321 | if out == nil { 322 | out = New(in[0].Shape...) 323 | } 324 | if !EqualShape(out, in...) { 325 | panic("narrays must have equal shape.") 326 | } 327 | 328 | divSlice(out.Data, in[0].Data, in[1].Data) 329 | 330 | // Multiply each following, if more than two arguments. 331 | for k := 2; k < len(in); k++ { 332 | divSlice(out.Data, out.Data, in[k].Data) 333 | } 334 | return out 335 | } 336 | 337 | // Sub subtracts narrays elementwise. 338 | // out = in[0] - in[1] - in[2] .... 339 | // Will panic if there are not at least two input narrays 340 | // or if narray shapes don't match. 341 | // If out is nil a new array is created. 342 | func Sub(out *NArray, in ...*NArray) *NArray { 343 | 344 | if len(in) < 2 { 345 | panic("not in enough arguments") 346 | } 347 | if out == nil { 348 | out = New(in[0].Shape...) 349 | } 350 | if !EqualShape(out, in...) { 351 | panic("narrays must have equal shape.") 352 | } 353 | 354 | subSlice(out.Data, in[0].Data, in[1].Data) 355 | 356 | // Multiply each following, if more than two arguments. 357 | for k := 2; k < len(in); k++ { 358 | subSlice(out.Data, out.Data, in[k].Data) 359 | } 360 | return out 361 | } 362 | 363 | // AddConst adds const to an narray elementwise. 364 | // out = in + c 365 | // If out is nil a new array is created. 366 | func AddConst(out *NArray, in *NArray, c {{.Format}}) *NArray { 367 | 368 | if out == nil { 369 | out = New(in.Shape...) 370 | } else { 371 | if !EqualShape(out, in) { 372 | panic("narrays must have equal shape.") 373 | } 374 | } 375 | caddSlice(out.Data, in.Data, c) 376 | return out 377 | } 378 | 379 | // AddScaled adds a scaled narray elementwise. 380 | // y = y + a * x 381 | // If y is nil a new array is created. 382 | func AddScaled(y *NArray, x *NArray, a {{.Format}}) *NArray { 383 | 384 | if y == nil { 385 | y = New(x.Shape...) 386 | } else { 387 | if !EqualShape(y, x) { 388 | panic("narrays must have equal shape.") 389 | } 390 | } 391 | addScaledSlice(y.Data, x.Data, a) 392 | return y 393 | } 394 | 395 | // Scale multiplies an narray by a factor elementwise. 396 | // out = c * in 397 | // If out is nil a new array is created. 398 | func Scale(out *NArray, in *NArray, c {{.Format}}) *NArray { 399 | 400 | if out == nil { 401 | out = New(in.Shape...) 402 | } else { 403 | if !EqualShape(out, in) { 404 | panic("narrays must have equal shape.") 405 | } 406 | } 407 | cmulSlice(out.Data, in.Data, c) 408 | return out 409 | } 410 | 411 | // Rcp returns reciprocal values of narrays elementwise. 412 | // out = 1.0 / in 413 | // If out is nil a new array is created. 414 | func Rcp(out, in *NArray) *NArray { 415 | if out == nil { 416 | out = New(in.Shape...) 417 | } else { 418 | if !EqualShape(out, in) { 419 | panic("narrays must have equal shape.") 420 | } 421 | } 422 | cdivSlice(out.Data, in.Data, 1.0) 423 | return out 424 | } 425 | 426 | // Sqrt returns square root values of narrays elementwise. 427 | // out = math.Sqrt(in) 428 | // If out is nil a new array is created. 429 | func Sqrt(out, in *NArray) *NArray { 430 | if out == nil { 431 | out = New(in.Shape...) 432 | } else { 433 | if !EqualShape(out, in) { 434 | panic("narrays must have equal shape.") 435 | } 436 | } 437 | sqrtSlice(out.Data, in.Data) 438 | return out 439 | } 440 | 441 | // Abs returns square root values of narrays elementwise. 442 | // out = math.Abs(in) 443 | // If out is nil a new array is created. 444 | func Abs(out, in *NArray) *NArray { 445 | if out == nil { 446 | out = New(in.Shape...) 447 | } else { 448 | if !EqualShape(out, in) { 449 | panic("narrays must have equal shape.") 450 | } 451 | } 452 | absSlice(out.Data, in.Data) 453 | return out 454 | } 455 | 456 | // Max returns the max value in the narray. 457 | func (na *NArray) Max() {{.Format}} { 458 | if na == nil || len(na.Data) == 0 { 459 | panic("unable to take max of nil or zero-sizes array") 460 | } 461 | return maxSliceElement(na.Data) 462 | } 463 | 464 | // MaxIdx returns the max value and corresponding indices. 465 | func (na *NArray) MaxIdx() ({{.Format}}, []int) { 466 | 467 | var offset int 468 | max := {{.Smallest}} 469 | for i := 0; i < len(na.Data); i++ { 470 | if na.Data[i] > max { 471 | max = na.Data[i] 472 | offset = i 473 | } 474 | } 475 | return max, na.ReverseIndex(offset) 476 | } 477 | 478 | // MaxArray compare input narrays and returns an narray containing 479 | // the element-wise maxima. 480 | // out[i,j,k,...] = max(in0[i,j,k,...], in1[i,j,k,...], ...) 481 | // Will panic if there are not at least two input narray 482 | // or if narray shapes don't match. 483 | // If out is nil a new array is created. 484 | func MaxArray(out *NArray, in ...*NArray) *NArray { 485 | 486 | if len(in) < 2 { 487 | panic("not in enough input narrays") 488 | } 489 | if out == nil { 490 | out = New(in[0].Shape...) 491 | } 492 | if !EqualShape(out, in...) { 493 | panic("narrays must have equal shape.") 494 | } 495 | 496 | maxSlice(out.Data, in[0].Data, in[1].Data) 497 | 498 | // Also add each following, if more than two arguments. 499 | for k := 2; k < len(in); k++ { 500 | maxSlice(out.Data, out.Data, in[k].Data) 501 | } 502 | return out 503 | } 504 | 505 | // Copysign returns values with the magnitude of a and the sign of b 506 | // for each element of the arrays. 507 | // Will panic if narray shapes don't match. 508 | // If out is nil a new array is created. 509 | func Copysign(out, a, b *NArray) *NArray { 510 | 511 | if out == nil { 512 | out = New(a.Shape...) 513 | } 514 | if !EqualShape(out, a, b) { 515 | panic("narrays must have equal shape.") 516 | } 517 | 518 | csignSlice(out.Data, a.Data, b.Data) 519 | 520 | return out 521 | } 522 | 523 | // Min returns the min value in the narray. 524 | func (na *NArray) Min() {{.Format}} { 525 | if na == nil || len(na.Data) == 0 { 526 | panic("unable to take min of nil or zero-sizes array") 527 | } 528 | return minSliceElement(na.Data) 529 | } 530 | 531 | // MinIdx returns the min value and corresponding indices. 532 | func (na *NArray) MinIdx() ({{.Format}}, []int) { 533 | 534 | var offset int 535 | min := {{.Biggest}} 536 | for i := 0; i < len(na.Data); i++ { 537 | if na.Data[i] < min { 538 | min = na.Data[i] 539 | offset = i 540 | } 541 | } 542 | return min, na.ReverseIndex(offset) 543 | } 544 | 545 | // MinArray compare input narrays and returns an narray containing 546 | // the element-wise minima. 547 | // out[i,j,k,...] = min(in0[i,j,k,...], in1[i,j,k,...], ...) 548 | // Will panic if there are not at least two input narray 549 | // or if narray shapes don't match. 550 | // If out is nil a new array is created. 551 | func MinArray(out *NArray, in ...*NArray) *NArray { 552 | 553 | if len(in) < 2 { 554 | panic("not in enough input narrays") 555 | } 556 | if out == nil { 557 | out = New(in[0].Shape...) 558 | } 559 | if !EqualShape(out, in...) { 560 | panic("narrays must have equal shape.") 561 | } 562 | 563 | minSlice(out.Data, in[0].Data, in[1].Data) 564 | 565 | // Also add each following, if more than two arguments. 566 | for k := 2; k < len(in); k++ { 567 | minSlice(out.Data, out.Data, in[k].Data) 568 | } 569 | 570 | return out 571 | } 572 | 573 | // Prod returns the products of all the elements in the narray. 574 | func (na *NArray) Prod() {{.Format}} { 575 | 576 | p := {{.Format}}(1.0) 577 | for _, v := range na.Data { 578 | p *= v 579 | } 580 | return p 581 | } 582 | 583 | // Sum returns the sum of all the elements in the narray. 584 | func (na *NArray) Sum() {{.Format}} { 585 | return sliceSum(na.Data) 586 | } 587 | 588 | // SetValue sets all elements to value. 589 | func (na *NArray) SetValue(v {{.Format}}) *NArray { 590 | 591 | for i := range na.Data { 592 | na.Data[i] = v 593 | } 594 | return na 595 | } 596 | 597 | 598 | // Encode converts values in-place as follows: 599 | // Inf to math.MaxFloat64 600 | // -Inf to -math.MaxFloat64 601 | // NaN ro 0 602 | // 603 | // Returns the indices of the modified values as follows: 604 | // Values in inf => na.Data[abs(v)] = sign(v) * Inf 605 | // Values in nan => na.Data[v] = NaN 606 | func (na *NArray) Encode() (inf, nan []int) { 607 | 608 | inf = []int{} 609 | nan = []int{} 610 | for k, v := range na.Data { 611 | switch { 612 | case math.IsInf(float64(v), 1): 613 | na.Data[k] = {{.Biggest}} 614 | inf = append(inf, k) 615 | case math.IsInf(float64(v), -1): 616 | na.Data[k] = {{.Smallest}} 617 | inf = append(inf, -k) 618 | case math.IsNaN(float64(v)): 619 | na.Data[k] = 0 620 | nan = append(nan, k) 621 | } 622 | } 623 | return 624 | } 625 | 626 | // Decode converts values in-place. 627 | // See Encode() for details. 628 | func (na *NArray) Decode(inf, nan []int) { 629 | pInf := {{.Format}}(math.Inf(1)) 630 | nInf := {{.Format}}(math.Inf(-1)) 631 | fNan := {{.Format}}(math.NaN()) 632 | 633 | for _, v := range inf { 634 | if v >= 0 { 635 | na.Data[v] = pInf 636 | } else { 637 | na.Data[-v] = nInf 638 | } 639 | } 640 | for _, v := range nan { 641 | na.Data[v] = fNan 642 | } 643 | } 644 | 645 | // SubArray returns an narray of lower rank as follows: 646 | // 647 | // Example, given an narray with shape 2x3x4 (rank=3), return the subarray 648 | // of rank=2 corresponding to dim[2]=1 649 | // 650 | // x := New(2,3,4) 651 | // y := x.SubArray(-1,-1,1) // use -1 to select a dimension. Put a 1 in dim=2 (third argument). 652 | // // y = {x(0,0,1), x(0,1,1), x(0,2,1), x(1,0,1), ...} 653 | // 654 | func (na *NArray) SubArray(query ...int) *NArray { 655 | 656 | if len(na.Shape) == 0 { 657 | panic("cannot get subarray from narray with rank=0") 658 | } 659 | 660 | qs := querySubset(query, na.Shape) 661 | var ns []int // new shape 662 | for k, v := range query { 663 | if v < 0 { 664 | ns = append(ns, na.Shape[k]) 665 | } 666 | } 667 | newArr := New(ns...) 668 | for k, v := range qs { 669 | newArr.Data[k] = na.At(v...) 670 | } 671 | 672 | return newArr 673 | } 674 | 675 | // Reshape returns an narray with a new shape. 676 | func (na *NArray) Reshape(dim ...int) *NArray { 677 | panic("not implemented") 678 | } 679 | 680 | // Sprint prints narray elements when f returns true. 681 | // index is the linear index of an narray. 682 | func (na *NArray) Sprint(f func(na *NArray, index int) bool) string { 683 | 684 | b := bytes.NewBufferString(fmt.Sprintln("narray rank: ", na.Rank)) 685 | _, _ = b.WriteString(fmt.Sprintln("narray shape: ", na.Shape)) 686 | for k, v := range na.Data { 687 | idx := na.ReverseIndex(k) 688 | if f(na, k) { 689 | _, _ = b.WriteString("[") 690 | for axis, av := range idx { 691 | _, _ = b.WriteString(formatted(av, na.Shape[axis]-1)) 692 | } 693 | _, _ = b.WriteString(fmt.Sprintf("] => %f\n", v)) 694 | } 695 | } 696 | return b.String() 697 | } 698 | 699 | // Read unmarshals json data from an io.Reader into an narray struct. 700 | func Read(r io.Reader) (*NArray, error) { 701 | dec := json.NewDecoder(r) 702 | var na NArray 703 | err := dec.Decode(&na) 704 | if err != nil && err != io.EOF { 705 | return nil, err 706 | } 707 | return &na, nil 708 | } 709 | 710 | // ReadFile unmarshals json data from a file into an narray struct. 711 | func ReadFile(fn string) (*NArray, error) { 712 | 713 | f, err := os.Open(fn) 714 | if err != nil { 715 | return nil, err 716 | } 717 | defer f.Close() 718 | return Read(f) 719 | } 720 | 721 | // Write writes narray to an io.Writer. 722 | func (na *NArray) Write(w io.Writer) error { 723 | 724 | enc := json.NewEncoder(w) 725 | err := enc.Encode(na) 726 | if err != nil { 727 | return err 728 | } 729 | return nil 730 | } 731 | 732 | // WriteFile writes an narray to a file. 733 | func (na *NArray) WriteFile(fn string) error { 734 | 735 | e := os.MkdirAll(filepath.Dir(fn), 0755) 736 | if e != nil { 737 | return e 738 | } 739 | f, err := os.Create(fn) 740 | if err != nil { 741 | return err 742 | } 743 | defer f.Close() 744 | 745 | ee := na.Write(f) 746 | if ee != nil { 747 | return ee 748 | } 749 | return nil 750 | } 751 | 752 | // ToJSON returns a json string. 753 | func (na *NArray) ToJSON() (string, error) { 754 | var b bytes.Buffer 755 | err := na.Write(&b) 756 | return b.String(), err 757 | } 758 | 759 | // MarshalJSON implements the json.Marshaller interface. 760 | // The custom marshaller is needed to encode Inf/NaN values. 761 | func (na *NArray) MarshalJSON() ([]byte, error) { 762 | 763 | ena := na.Copy() 764 | inf, nan := ena.Encode() 765 | return json.Marshal(struct { 766 | Rank int `json:"rank"` 767 | Shape []int `json:"shape"` 768 | Data []{{.Format}} `json:"data"` 769 | Strides []int `json:"strides"` 770 | Inf []int `json:"inf,omitempty"` 771 | NaN []int `json:"nan,omitempty"` 772 | }{ 773 | Rank: ena.Rank, 774 | Shape: ena.Shape, 775 | Data: ena.Data, 776 | Strides: ena.Strides, 777 | Inf: inf, 778 | NaN: nan, 779 | }) 780 | } 781 | 782 | // UnmarshalJSON implements the json.Unarshaller interface. 783 | // The custom unmarshaller is needed to decode Inf/NaN values. 784 | func (na *NArray) UnmarshalJSON(b []byte) error { 785 | x := struct { 786 | Rank int `json:"rank"` 787 | Shape []int `json:"shape"` 788 | Data []{{.Format}} `json:"data"` 789 | Strides []int `json:"strides"` 790 | Inf []int `json:"inf,omitempty"` 791 | NaN []int `json:"nan,omitempty"` 792 | }{} 793 | 794 | err := json.Unmarshal(b, &x) 795 | if err != nil { 796 | return err 797 | } 798 | 799 | na.Rank = x.Rank 800 | na.Shape = x.Shape 801 | na.Data = x.Data 802 | na.Strides = x.Strides 803 | na.Decode(x.Inf, x.NaN) 804 | return nil 805 | } 806 | 807 | // String prints the narray 808 | func (na *NArray) String() string { 809 | 810 | return na.Sprint(func(na *NArray, k int) bool { 811 | return true 812 | }) 813 | } 814 | 815 | // equal returns true if |x-y|/(|avg(x,y)|+1) < tol. 816 | func equal(x, y {{.Format}}, tol float64) bool { 817 | avg := (math.Abs(float64(x+y)) / 2.0) 818 | sErr := math.Abs(float64(x-y)) / (avg + 1) 819 | if sErr > tol { 820 | return false 821 | } 822 | return true 823 | } 824 | 825 | // EqualValues compares two narrays elementwise. 826 | // Returns true if for all elements |x-y|/(|avg(x,y)|+1) < tol. 827 | func EqualValues(x *NArray, y *NArray, tol float64) bool { 828 | if !EqualShape(x, y) { 829 | panic("narrays must have equal shape.") 830 | } 831 | for i, _ := range x.Data { 832 | if !equal(x.Data[i], y.Data[i], tol) { 833 | return false 834 | } 835 | } 836 | return true 837 | } 838 | 839 | func formatted(n, max int) string { 840 | b := bytes.NewBufferString(" ") 841 | for i := 0; i < nd(max)-nd(n); i++ { 842 | _, _ = b.WriteString(" ") 843 | } 844 | _, _ = b.WriteString(strconv.FormatInt(int64(n), 10)) 845 | return b.String() 846 | } 847 | 848 | // num digits in number 849 | func nd(n int) int { 850 | if n == 0 { 851 | return 1 852 | } 853 | return int(math.Log10(float64(n))) + 1 854 | } 855 | 856 | func cartesianProduct(s []int) [][]int { 857 | 858 | if len(s) == 1 { 859 | z := make([][]int, s[0], s[0]) 860 | for k := range z { 861 | z[k] = []int{k} 862 | } 863 | return z 864 | } 865 | var result [][]int 866 | for i := 0; i < s[0]; i++ { 867 | x := cartesianProduct(s[1:]) 868 | for _, v := range x { 869 | var sl []int 870 | sl = append(sl, i) 871 | sl = append(sl, v...) 872 | result = append(result, sl) 873 | } 874 | } 875 | return result 876 | } 877 | 878 | // Recursively find indices for query q. 879 | // Helper func to generate narray subsets. 880 | func querySubset(q, s []int) [][]int { 881 | 882 | if len(q) != len(s) { 883 | panic("size mismatch") 884 | } 885 | var result [][]int 886 | 887 | switch { 888 | case len(s) == 1 && q[0] >= 0: 889 | result = [][]int{[]int{q[0]}} 890 | 891 | case len(s) == 1 && q[0] < 0: 892 | result = make([][]int, s[0], s[0]) 893 | for k := range result { 894 | result[k] = []int{k} 895 | } 896 | 897 | case q[0] >= 0: 898 | x := querySubset(q[1:], s[1:]) 899 | for _, v := range x { 900 | var sl []int 901 | sl = append(sl, q[0]) 902 | sl = append(sl, v...) 903 | result = append(result, sl) 904 | } 905 | 906 | case q[0] < 0: 907 | for i := 0; i < s[0]; i++ { 908 | x := querySubset(q[1:], s[1:]) 909 | for _, v := range x { 910 | var sl []int 911 | sl = append(sl, i) 912 | sl = append(sl, v...) 913 | result = append(result, sl) 914 | } 915 | } 916 | } 917 | return result 918 | } 919 | -------------------------------------------------------------------------------- /testfmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Note the set -ev at the top. 4 | # The -e flag causes the script to exit as soon as one command returns a non-zero exit code. 5 | # This can be handy if you want whatever script you have to exit early. 6 | # It also helps in complex installation scripts where one failed command wouldn’t otherwise cause the installation to fail. 7 | # The -v flag makes the shell print all lines in the script before executing them, 8 | # which helps identify which steps failed. 9 | set -ev 10 | 11 | if [[ -n $(gofmt -d .) ]]; then 12 | gofmt -d . 13 | exit 1 14 | fi 15 | 16 | if [[ -n $(gofmt -d ./na64) ]]; then 17 | gofmt -d ./na64 18 | exit 1 19 | fi 20 | 21 | if [[ -n $(gofmt -d ./na32) ]]; then 22 | gofmt -d ./na32 23 | exit 1 24 | fi 25 | --------------------------------------------------------------------------------