Coder Social home page Coder Social logo

gorgonia / tensor Goto Github PK

View Code? Open in Web Editor NEW
346.0 22.0 49.0 1.54 MB

package tensor provides efficient and generic n-dimensional arrays in Go that are useful for machine learning and deep learning purposes

License: Apache License 2.0

Go 99.92% Assembly 0.03% Shell 0.05%
tensor ndarray multidimensional-arrays deep-learning machine-learning golang hacktoberfest

tensor's Introduction

Package tensor GoDoc GitHub version Build Status Coverage Status Go Report Card unstable#

Package tensor is a package that provides efficient, generic (by some definitions of generic) n-dimensional arrays in Go. Also in this package are functions and methods that are used commonly in arithmetic, comparison and linear algebra operations.

The main purpose of this package is to support the operations required by Gorgonia.

Introduction

In the data analysis world, Numpy and Matlab currently reign supreme. Both tools rely heavily on having performant n-dimensional arrays, or tensors. There is an obvious need for multidimensional arrays in Go.

While slices are cool, a large majority of scientific and numeric computing work relies heavily on matrices (two-dimensional arrays), three dimensional arrays and so on. In Go, the typical way of getting multidimensional arrays is to use something like [][]T. Applications that are more math heavy may opt to use the very excellent Gonum matrix package. What then if we want to go beyond having a float64 matrix? What if we wanted a 3-dimensional float32 array?

It comes to reason then there should be a data structure that handles these things. The tensor package fits in that niche.

Basic Idea: Tensor

A tensor is a multidimensional array. It's like a slice, but works in multiple dimensions.

With slices, there are usage patterns that are repeated enough that warrant abstraction - append, len, cap, range are abstractions used to manipulate and query slices. Additionally slicing operations (a[:1] for example) are also abstractions provided by the language. Andrew Gerrand wrote a very good write up on Go's slice usage and internals.

Tensors come with their own set of usage patterns and abstractions. Most of these have analogues in slices, enumerated below (do note that certain slice operation will have more than one tensor analogue - this is due to the number of options available):

Slice Operation Tensor Operation
len(a) T.Shape()
cap(a) T.DataSize()
a[:] T.Slice(...)
a[0] T.At(x,y)
append(a, ...) T.Stack(...), T.Concat(...)
copy(dest, src) T.CopyTo(dest), tensor.Copy(dest, src)
for _, v := range a for i, err := iterator.Next(); err == nil; i, err = iterator.Next()

Some operations for a tensor does not have direct analogues to slice operations. However, they stem from the same idea, and can be considered a superset of all operations common to slices. They're enumerated below:

Tensor Operation Basic idea in slices
T.Strides() The stride of a slice will always be one element
T.Dims() The dimensions of a slice will always be one
T.Size() The size of a slice will always be its length
T.Dtype() The type of a slice is always known at compile time
T.Reshape() Given the shape of a slice is static, you can't really reshape a slice
T.T(...) / T.Transpose() / T.UT() No equivalent with slices

The Types of Tensors

As of the current revision of this package, only dense tensors are supported. Support for sparse matrix (in form of a sparse column matrix and dictionary of keys matrix) will be coming shortly.

Dense Tensors

The *Dense tensor is the primary tensor and is represented by a singular flat array, regardless of dimensions. See the Design of *Dense section for more information. It can hold any data type.

Compressed Sparse Column Matrix

Documentation Coming soon

Compressed Sparse Row Matrix

Documentation Coming soon

Usage

To install: go get -u "gorgonia.org/tensor"

To create a matrix with package tensor is easy:

// Creating a (2,2) matrix of int:
a := New(WithShape(2, 2), WithBacking([]int{1, 2, 3, 4}))
fmt.Printf("a:\n%v\n", a)

// Output:
// a:
// ⎡1  2⎤
// ⎣3  4⎦
//

To create a 3-Tensor is just as easy - just put the correct shape and you're good to go:

// Creating a (2,3,4) 3-Tensor of float32
b := New(WithBacking(Range(Float32, 0, 24)), WithShape(2, 3, 4))
fmt.Printf("b:\n%1.1f\n", b)

// Output:
// b:
// ⎡ 0.0   1.0   2.0   3.0⎤
// ⎢ 4.0   5.0   6.0   7.0⎥
// ⎣ 8.0   9.0  10.0  11.0⎦
//
// ⎡12.0  13.0  14.0  15.0⎤
// ⎢16.0  17.0  18.0  19.0⎥
// ⎣20.0  21.0  22.0  23.0⎦

Accessing and Setting data is fairly easy. Dimensions are 0-indexed, so if you come from an R background, suck it up like I did. Be warned, this is the inefficient way if you want to do a batch access/setting:

// Accessing data:
b := New(WithBacking(Range(Float32, 0, 24)), WithShape(2, 3, 4))
x, _ := b.At(0, 1, 2)
fmt.Printf("x: %v\n", x)

// Setting data
b.SetAt(float32(1000), 0, 1, 2)
fmt.Printf("b:\n%v", b)

// Output:
// x: 6
// b:
// ⎡   0     1     2     3⎤
// ⎢   4     5  1000     7⎥
// ⎣   8     9    10    11⎦

// ⎡  12    13    14    15⎤
// ⎢  16    17    18    19⎥
// ⎣  20    21    22    23⎦

Bear in mind to pass in data of the correct type. This example will cause a panic:

// Accessing data:
b := New(WithBacking(Range(Float32, 0, 24)), WithShape(2, 3, 4))
x, _ := b.At(0, 1, 2)
fmt.Printf("x: %v\n", x)

// Setting data
b.SetAt(1000, 0, 1, 2)
fmt.Printf("b:\n%v", b)

There is a whole laundry list of methods and functions available at the godoc page

Design of *Dense

The design of the *Dense tensor is quite simple in concept. However, let's start with something more familiar. This is a visual representation of a slice in Go (taken from rsc's excellent blog post on Go data structures):

slice

The data structure for *Dense is similar, but a lot more complex. Much of the complexity comes from the need to do accounting work on the data structure as well as preserving references to memory locations. This is how the *Dense is defined:

type Dense struct {
	*AP
	array
	e Engine

	// other fields elided for simplicity's sake
}

And here's a visual representation of the *Dense.

dense

*Dense draws its inspiration from Go's slice. Underlying it all is a flat array, and access to elements are controlled by *AP. Where a Go is able to store its metadata in a 3-word structure (obviating the need to allocate memory), a *Dense unfortunately needs to allocate some memory. The majority of the data is stored in the *AP structure, which contains metadata such as shape, stride, and methods for accessing the array.

*Dense embeds an array (not to be confused with Go's array), which is an abstracted data structure that looks like this:

type array struct {
	storage.Header
	t Dtype
	v interface{}
}

*storage.Header is the same structure as reflect.SliceHeader, except it stores a unsafe.Pointer instead of a uintptr. This is done so that eventually when more tests are done to determine how the garbage collector marks data, the v field may be removed.

The storage.Header field of the array (and hence *Dense) is there to provide a quick and easy way to translate back into a slice for operations that use familiar slice semantics, of which much of the operations are dependent upon.

By default, *Dense operations try to use the language builtin slice operations by casting the *storage.Header field into a slice. However, to accomodate a larger subset of types, the *Dense operations have a fallback to using pointer arithmetic to iterate through the slices for other types with non-primitive kinds (yes, you CAN do pointer arithmetic in Go. It's slow and unsafe). The result is slower operations for types with non-primitive kinds.

Memory Allocation

New() functions as expected - it returns a pointer of *Dense to a array of zeroed memory. The underlying array is allocated, depending on what ConsOpt is passed in. With New(), ConsOpts are used to determine the exact nature of the *Dense. It's a bit icky (I'd have preferred everything to have been known statically at compile time), but it works. Let's look at some examples:

x := New(Of(Float64), WithShape(2,2)) // works
y := New(WithShape(2,2)) // panics
z := New(WithBacking([]int{1,2,3,4})) // works

The following will happen:

  • Line 1 works: This will allocate a float64 array of size 4.
  • Line 2 will cause a panic. This is because the function doesn't know what to allocate - it only knows to allocate an array of something for the size of 4.
  • Line 3 will NOT fail, because the array has already been allocated (the *Dense reuses the same backing array as the slice passed in). Its shape will be set to (4).

Alternatively you may also pass in an Engine. If that's the case then the allocation will use the Alloc method of the Engine instead:

x := New(Of(Float64), WithEngine(myEngine), WithShape(2,2))

The above call will use myEngine to allocate memory instead. This is useful in cases where you may want to manually manage your memory.

Other failed designs

The alternative designs can be seen in the ALTERNATIVE DESIGNS document

Generic Features

Example:

x := New(WithBacking([]string{"hello", "world", "hello", "world"}), WithShape(2,2))
x = New(WithBacking([]int{1,2,3,4}), WithShape(2,2))

The above code will not cause a compile error, because the structure holding the underlying array (of strings and then of ints) is a *Dense.

One could argue that this sidesteps the compiler's type checking system, deferring it to runtime (which a number of people consider dangerous). However, tools are being developed to type check these things, and until Go does support typechecked generics, unfortunately this will be the way it has to be.

Currently, the tensor package supports limited type of genericity - limited to a tensor of any primitive type.

How This Package is Developed

Much of the code in this package is generated. The code to generate them is in the directory genlib2. genlib2 requires goimports binary to be available in the $PATH.

Tests

Tests require python with numpy installed. You can select which python intepreter is being used by setting the environment variable PYTHON_COMMAND accordingly. The default value is python.

Things Knowingly Untested For

  • complex64 and complex128 are excluded from quick check generation process Issue #11

TODO

  • Identity optimizations for op
  • Zero value optimizations
  • fix Random() - super dodgy

How To Get Support

The best way of support right now is to open a ticket on Github.

Contributing

Obviously since you are most probably reading this on Github, Github will form the major part of the workflow for contributing to this package.

See also: CONTRIBUTING.md

Contributors and Significant Contributors

All contributions are welcome. However, there is a new class of contributor, called Significant Contributors.

A Significant Contributor is one who has shown deep understanding of how the library works and/or its environs. Here are examples of what constitutes a Significant Contribution:

  • Wrote significant amounts of documentation pertaining to why/the mechanics of particular functions/methods and how the different parts affect one another
  • Wrote code, and tests around the more intricately connected parts of Gorgonia
  • Wrote code and tests, and have at least 5 pull requests accepted
  • Provided expert analysis on parts of the package (for example, you may be a floating point operations expert who optimized one function)
  • Answered at least 10 support questions.

Significant Contributors list will be updated once a month (if anyone even uses Gorgonia that is).

Licence

Gorgonia and the tensor package are licenced under a variant of Apache 2.0. It's for all intents and purposes the same as the Apache 2.0 Licence, with the exception of not being able to commercially profit directly from the package unless you're a Significant Contributor (for example, providing commercial support for the package). It's perfectly fine to profit directly from a derivative of Gorgonia (for example, if you use Gorgonia as a library in your product)

Everyone is still allowed to use Gorgonia for commercial purposes (example: using it in a software for your business).

Various Other Copyright Notices

These are the packages and libraries which inspired and were adapted from in the process of writing Gorgonia (the Go packages that were used were already declared above):

Source How it's Used Licence
Numpy Inspired large portions. Directly adapted algorithms for a few methods (explicitly labelled in the docs) MIT/BSD-like. Numpy Licence

tensor's People

Contributors

bdleitner avatar bezineb5 avatar cfgt avatar chewxy avatar cpllbstr avatar dcu avatar dependabot[bot] avatar jlauinger avatar khezen avatar ksw2000 avatar markkremer avatar mlg556 avatar owulveryck avatar pointlander avatar poopoothegorilla avatar strigi-form avatar tiagorlampert avatar wzzhu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tensor's Issues

Typechecked generic tensor?

Ref this quote from https://github.com/gorgonia/tensor?tab=readme-ov-file#generic-features:

One could argue that this sidesteps the compiler's type checking system, deferring it to runtime (which a number of people consider dangerous). However, tools are being developed to type check these things, and until Go does support typechecked generics, unfortunately this will be the way it has to be.
Currently, the tensor package supports limited type of genericity - limited to a tensor of any primitive type.

Go does have typechecked generics as of 1.18. I'm sure you've thought more about this than I have, so I'm very curious to hear your thoughts on templating the Tensor interface by T, so you'd write Tensor[float32] for example. It would allow writing functions operating only on float tensors like so: func dostuff[T Float](t Tensor[T]) Tensor[T]. Another consequence would be Apply taking a function of func (v T) T, rather than func (v interface{}) interface{}).

Iterator.Chan() considered harmful

From @chewxy on August 13, 2017 2:9

sketch space for describing how to create a chan int of negative length, and how to reproduce it

Background/Context of the Issue

Gorgonia is a library for representing and executing mathematical equations, and performing automatic differentiation. It's like Tensorflow and PyTorch for Go. It's currently undergoing some major internal refactor (that will not affect the public APIs much)

I was improving the backend tensor package by splitting up the data structure into a data structure + pluggable execution engine, instead of having built in methods (see also #128). The reasons are so that it's easier to change out execution backends (CPU, GPU... even a network CPU (actual experiment I did was to run a small neural network on a Raspberry Pi and all computation is offshored to my workstation, and vice versa, which turned out to be a supremely bad idea)).

Another reason was due to the fact that I wanted to do some experiments at my work which use algorithms that involve sparse tensors (see also #127) for matrix factorization tasks.

Lastly, I wanted to clean up the generics support of the tensor package. The current master branch of the tensor package had a lot of code to support arbitrary tensor types. With the split of execution engines and data structure, more of this support could be offloaded to the execution engine instead. This package provides a default execution engine (type StdEng struct{}: https://github.com/chewxy/gorgonia/blob/debugrace/tensor/defaultengine.go), which could be extended (example: https://github.com/chewxy/gorgonia/blob/debugrace/tensor/example_extension_test.go) . The idea was to have an internal/execution package which held all the code for the default execution engine.

Data Structures

The most fundamental data structure is storage.Header, which is an analogue for a Go slice: it's a three word structure. It's chosen because it is a ridiculously simple structure can store Go-allocated memory, C-allocated memory and device-allocated memory (like CUDA).

On top of storage.Header is tensor.array. It's essentially a storage.Header with an additional field for the type. The v field will eventually be phased out once the refactor is complete.

On top of tensor.array are the various implementations of tensor.Tensor. Chief amongst these is the tensor.Dense struct. Essentially it's a tensor.array coupled with some access patterns and meta information.

Access to the data in the tensor.Tensor can be achieved by use of Iterators. The Iterator basically assumes that the data is held in a flat slice, and returns the next index on the slice. There are auxiliary methods like NextValidity to handle special case tensors like masked tensors, where some elements are masked from operations.

The bug happens in the Chan method of the FlatIterator type.

How to reproduce

The branch where the bug is known to exist is the debugrace branch, which can be found here: 1dee6d2 .

  1. git checkout debugrace
  2. Run tests with various GOMAXPROCS like so: GOMAXPROCS=1 go test -run=. . Try it with various GOMAXPROCS, one of them is bound to trigger an issue.
  3. The test won't panic, because I have added a recover statement here https://github.com/chewxy/gorgonia/blob/debugrace/tensor/dense_viewstack_specializations.go#L636. Removing the deferred function causes a index out of bounds panic.
  4. All the tests must be run to trigger the issue.
  5. The issue is found in the test for the Stack function: https://github.com/chewxy/gorgonia/blob/debugrace/tensor/dense_matop_test.go#L768 . If only the stack test is run (for example GOMAXPROCS=1 go test -run=Stack), it is unlikely the problem will show up (I wrote a tiny python script to run it as many times as possible with many GOMAXPROCS configurations and none of them caused an error).

You should get something like this:

image

Environments

I've managed to reproduce the issue on OS X, with Go 1.8 and on Ubuntu 16.10 with Go 1.8.2 and Go tip (whatever gvm thinks is Go tip). I've no access to Go on a windows box so I can't test it on Windows.

Magic and Unsafe Use

As part of the refactoring, there are a few magic bits being used. Here I attempt to list them all (may not be exhaustive):

What I suspect

I suspect that there may be some naughty things happening in memory (because it only happens when all the tests are run). The problem is I don't know exactly where to start looking.

Copied from original issue: gorgonia/gorgonia#135

`*FlatIterator` Support For ColMajor

The *FlatIterator type implements Iterator, which is an interface for stateful iterators. It also supports reverse iteration (by means of the .SetReverse() method).

Currently, the *FlatIterator type supports the following operations

- Row Major Vector Col Major Vector Row Major N-DImensional Col Major N-Dimensional
Forward Next()
Backward Next()
Forward NextValidity()
Backward NextValidity()
Forward NextValid()
Backward NextValid()
Forward NextInvalid()
Backward NextInvalid()

A major internal method that underpins all of these are .ndNext() and .ndPrevious(). The corresponding col-major version are .colMajorNDnext() and .colMajorNDPrevious().

.colMajorNDPrevious() is not implemented. It can be found at Line 402 of iterator.go

MulScalar bug

@bezineb5 discovered this and opened #51

The bug is described as follows:

a2 := New(WithBacking([]float64{2}))
	b2 := New(WithBacking([]float64{3}))
	var correct interface{} = 6.0

	res, err := Mul(a2, b2)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}
	assert.Equal(t, correct, res.Data())
	t.Logf("a2 %v b2 %v, res %v", a2, b2, res)
	a := New(WithBacking([]float64{3, 2}))
	b := New(WithBacking([]float64{2}))
	correct := []float64{6, 4}

	res, err := Mul(a, b)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}
	assert.Equal(t, correct, res.Data())
	t.Logf("a %v b %v, res %v", a, b, res)

	// Test commutativity
	res, err = Mul(b, a)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}
	assert.Equal(t, correct, res.Data())
	t.Logf("a %v b %v, res %v", a, b, res)

The bug happens in https://github.com/gorgonia/tensor/blob/master/defaultengine_arith.go#L665 and https://github.com/gorgonia/tensor/blob/master/defaultengine_arith.go#L639 as well.

The bug affects all generated SV and VS arithmetic functions.

Upgrade dependency "github.com/google/flatbuffers"

Background

Repo github.com/gorgonia/tensor depends on github.com/google/[email protected].

https://github.com/gorgonia/tensor/blob/master/go.mod#L11

However, comparing version v1.12.0 of github.com/google/flatbuffers from proxy.golang.org and github, there are inconsistencies.

commit time of the copy on github.com

"committer": {
      "name": "Wouter van Oortmerssen",
      "email": "[email protected]",
      "date": "2020-03-12T22:33:39Z"
    }

commit time of the copy on proxy.golang.org

{"Version":"v1.12.0","Time":"2020-03-12T21:45:27Z"}

So the checksum from the code in github does not match the checksum saved in sum.golang.org. The v1.12.0 tag of github.com/google/flatbuffers might have been retagged after a minor edition on github. I guess you use proxy.golang.org to get dependencies, but that also shows that your project is depending on the copy of github.com/google/[email protected] before its edition. Depending upon such inconsistent tag version may also result in some unexpected errors as well as build errors due to different proxy settings.

For example, when someone who does not use proxy.golang.org, say GOPROXY=direct, attempts to get github.com/google/[email protected], the following error occurs.

go: downloading github.com/google/flatbuffers v1.12.0
go: github.com/google/flatbuffers@v1.12.0: verifying module: checksum mismatch
        downloaded: h1:N8EguYFm2wwdpoNcpchQY0tPs85vOJkboFb2dPxmixo=
        sum.golang.org: h1:/PtAHvnBY4Kqnx/xCQ3OIV9uYcSFGScBsWI3Oogeh6w=

SECURITY ERROR
This download does NOT match the one reported by the checksum server.
The bits may have been replaced on the origin server, or an attacker may
have intercepted the download attempt.

For more information, see 'go help module-auth'.

So, this is a reminder in the hope that you can get rid of this problematic version of project github.com/google/flatbuffers.

Solution

1. Bump the version of dependency github.com/google/flatbuffers

I would recommend bumping the version of github.com/google/flatbuffers to a new release, say v1.12.1, to ensure dependency copy in proxy.golang.org and github in sync.

References

"array2 is incomplete (or unallocatable); stack allocation disallowed" from examples

I'm getting this error with the example from the Gorgonia website:

gorgonia.org/tensor

../../../../go/pkg/mod/gorgonia.org/[email protected]/array.go:24:7: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../../go/pkg/mod/gorgonia.org/[email protected]/array.go:168:38: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../../go/pkg/mod/gorgonia.org/[email protected]/array.go:298:2: rawdata is incomplete (or unallocatable); stack allocation disallowed
../../../../go/pkg/mod/gorgonia.org/[email protected]/array.go:371:3: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../../go/pkg/mod/gorgonia.org/[email protected]/array.go:372:3: array2 is incomplete (or unallocatable); stack allocation disallowed

I'm running on macOS 10.13.6 High Sierra with go1.15.3 darwin/amd64 on a 2010 machine.

Can I quickly verify this isn't an issue with environment setup or platform? I've checked for the response on another machine from 2013 running macOS 10.15.7 and it's still present.

The example I'm using is as follows:

package main
import (
  "fmt"
  "log"
  "gorgonia.org/gorgonia"
)

func main(){
  g := NewGraph()

  var a, c, c *Node
  var err error

  a = NewScalar(g, Float64, WithName("a"))
  b = NewScalar(g, Float64, WithName("b"))

  c, err = Add(a, b)

  if err != nil {
    log.Fatal(err)
  }

  machine := NewTapeMachine(g)

  Let(a, 1.0)
  Let(b, 2.0)
  if machine.RunAll() != nil {
    log.Fatal(err)
  }
  fmt.Printf("%v", c.Value())

}```

Clarify Semantics breaks Slicing on vectors with shape 1

To reproduce:

	v = tensor.New(
		tensor.WithShape(1),
		tensor.WithBacking([]float64{2}),
	)
	sliced, err = v.Slice(gorgonia.S(0))
	handleErr(err)
	fmt.Printf("[0:1]\n%v\n%v\n\n", sliced.Shape(), sliced)

yields

panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
gorgonia.org/tensor.(*AP).S(0xc000298400, 0x1, 0xc000159e98, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/dcuadrado/go/src/gorgonia.org/tensor/ap.go:243 +0xcfc

h/t @dcu

Random index out of range [0] with length 0 error in some tests

Follow up on #108 on why the test failed randomly. I got the following error:

--- FAIL: TestFloat64Engine_makeArray (0.00s)
panic: runtime error: index out of range [0] with length 0 [recovered]
	panic: runtime error: index out of range [0] with length 0

goroutine 128 [running]:
testing.tRunner.func1.1(0x14a0a80, 0xc000244080)
	/opt/hostedtoolcache/go/1.14.15/x64/src/testing/testing.go:999 +0x461
testing.tRunner.func1(0xc00045c240)
	/opt/hostedtoolcache/go/1.14.15/x64/src/testing/testing.go:1002 +0x606
panic(0x14a0a80, 0xc000244080)
	/opt/hostedtoolcache/go/1.14.15/x64/src/runtime/panic.go:975 +0x3e3
gorgonia.org/tensor.(*array).Uintptr(...)
	/home/runner/work/tensor/tensor/array.go:158
gorgonia.org/tensor.array.Data(0x1e438f0, 0x0, 0x0, 0x169fc40, 0x13eb260, 0x13f33f7, 0x13f33e0)
	/home/runner/work/tensor/tensor/array.go:167 +0x211
gorgonia.org/tensor.TestFloat64Engine_makeArray.func1(0x0, 0x0)
	/home/runner/work/tensor/tensor/defaultenginefloat64_test.go:26 +0x199
reflect.Value.call(0x140fb00, 0xc000466040, 0x13, 0x14f71eb, 0x4, 0xc0001b8000, 0x1, 0x1, 0x49d7c2, 0x53aa60, ...)
	/opt/hostedtoolcache/go/1.14.15/x64/src/reflect/value.go:460 +0x967
reflect.Value.Call(0x140fb00, 0xc000466040, 0x13, 0xc0001b8000, 0x1, 0x1, 0xc000188000, 0x0, 0x0)
	/opt/hostedtoolcache/go/1.14.15/x64/src/reflect/value.go:321 +0xd4
testing/quick.Check(0x140fb00, 0xc000466040, 0x0, 0x4dce27, 0x47817b)
	/opt/hostedtoolcache/go/1.14.15/x64/src/testing/quick/quick.go:290 +0x27a
gorgonia.org/tensor.TestFloat64Engine_makeArray(0xc00045c240)
	/home/runner/work/tensor/tensor/defaultenginefloat64_test.go:38 +0xac
testing.tRunner(0xc00045c240, 0x1534a08)
	/opt/hostedtoolcache/go/1.14.15/x64/src/testing/testing.go:1050 +0x1ec
created by testing.(*T).Run
	/opt/hostedtoolcache/go/1.14.15/x64/src/testing/testing.go:1095 +0x538

After looking into it, it seems that quick.Check passes a random value for the size parameter to the test function. In this specific instance that random value was 0. This size is then used to create an array. And some of the functions don't work if the array has a length of 0.

Fun fact: this has a 1 in 65536 chance of occurring. This happened to me twice now. I don't think the chances are evenly distributed.

This can be fixed two ways:

  • Make array work with 0 as a size. (probably hard to do while keeping it efficient)
  • Make sure the tests don't pass in 0. And make sure that the makeArray doesn't get called with 0 (or less) as its size argument by returning an error earlier.

I think there are some other tests that can cause similar errors.

I am not working on fixing this at the moment so feel free to work on this issue if you're interested.

unable to get gorgonia.org/[email protected] ...

I am trying to run a gorgonia app that import gorgonia/tensor. in trying to get the tensor v0.9.10, i am getting the following error...
go: gorgonia.org/tensor: gorgonia.org/[email protected]: parsing go.mod: go.mod:6: require github.com/apache/arrow/go/arrow: version "latest" invalid: must be of the form v1.2.3

I am running go version 1.13.6 on macOS Mojave

Any help with resolving this would be greatly appreciated. Thanks in advance - jay

Go Version 1.15.3

Hello,

There appears to be an issue w.r.t Go v1.15.3. The code to reproduce the below was a simple copy/paste from the README or Gorgonia. Please see the below trace;

# gorgonia.org/tensor
../../../go/src/gorgonia.org/tensor/array.go:24:7: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:168:38: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:298:2: rawdata is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:371:3: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:372:3: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/defaultengine_matop_misc.go:211:8: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/defaultengine_matop_misc.go:220:5: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:376:19: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/array.go:377:19: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/dense.go:608:45: array2 is incomplete (or unallocatable); stack allocation disallowed
../../../go/src/gorgonia.org/tensor/dense.go:608:45: too many errors

I'm new to Go, and looking to port a few things over from TF. I rolled back to 1.13.9 and of course have no problems (haven't tested any intermediate versions). I had a quick look at the change logs from v1.14 -> v1.15 where it mentions making some allocations mandatory so I'm assuming it has to do with storage.Header inside the array2 struct, but like I said i'm new to the language so would be in no place to suggest a fix myself.

Thanks 😄

Import failure

go get -u "github.com/gorgonia/tensor"

return an error of

package github.com/gorgonia/tensor: code in directory C:\go\src\github.com\gorgonia\tensor expects import "gorgonia.org/tensor"

Dense Iterator off by 1

It appears that in 3x3x3 tensor while iterating the iterator Start is not pointing to the first element so that all values are off by one index.

⎡26   0   1⎤
⎢ 2   3   4⎥
⎣ 5   6   7⎦

⎡ 8   9  10⎤
⎢11  12  13⎥
⎣14  15  16⎦

⎡17  18  19⎤
⎢20  21  22⎥
⎣23  24  25⎦

CODE:

import (
	. "fmt"
	t "gorgonia.org/tensor"
)

func main() {
	a := t.New(t.WithShape(3, 3, 3), t.WithBacking(make([]int, 3*3*3)))
	it := t.IteratorFromDense(a)
	for i, err := it.Start(); err == nil; i, err = it.Next() {
		a.SetAt(i, it.Coord()[0], it.Coord()[1], it.Coord()[2])
	}
	Println(a)
}

Why not sync.Pool

???

  1. Write a sync.Pool version
  2. Benchmark against channel pool

Compare the meercats

Split *Dense into *Dense and *MaskedDense

From @chewxy on June 27, 2017 2:0

@kabaka0 thoughts? The idea is to as per #128 - the tensor package holds the data structures, and the essential methods (basically implementing Tensor), while things like Add will get moved to tensor/ops

Tasks

  • Write tests to ensure that all the new methods and functions in internal/stdeng can be accomplished using FlatMaskedIterator
  • If yes, then split into *Dense and *MaskedDense

Copied from original issue: gorgonia/gorgonia#131

Guidelines on use of Borrow and Return

From @kabaka0 on March 22, 2017 14:50

For consistency, it would be good to have guidelines on when to use Borrow in contributed code. It would be great if Borrow/Return could be the default for allocation (including new types going forward)- it would make contributing to the code easier, as well as make it easier to manage performance. You could probably do this without too much of a performance hit, although the Borrow/Return functions would need to become a bit more complex to make sure that returns only correspond to borrowed objects. Thoughts on this?

Copied from original issue: gorgonia/gorgonia#100

tensor.Mul bug

The tenor.Mul has bug in multiplying tensor with shape(1, 1). See the test code below. This might be caused by #54

The reason is that e.E.Mul(typ, dataA, retVal.hdr()) always assuming the result would be kept in dataA.

func TestMul(t *testing.T) {
	a := 2.0
	b := tensor.NewDense(tensor.Float64, tensor.Shape{1, 1}, tensor.WithBacking([]float64{3}))
	var correct interface{} = []float64{6.0}

	res, err := tensor.Mul(a, b)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}
	assert.Equal(t, correct, res.Data())
	t.Logf("a %v b %v, res %v", a, b, res)
}

FromScalar makes no sense with mask

From @chewxy on July 3, 2017 23:12

// FromScalar is a construction option for representing a scalar value as a Tensor
func FromScalar(x interface{}, argMask ...[]bool) ConsOpt {
	var mask []bool
	if len(argMask) > 0 {
		mask = argMask[0]
	}

	f := func(t Tensor) {
		switch tt := t.(type) {
		case *Dense:
			xt := reflect.TypeOf(x)
			xv := reflect.New(xt)
			xvi := reflect.Indirect(xv)
			xvi.Set(reflect.ValueOf(x))
			ptr := xv.Pointer()
			uptr := unsafe.Pointer(ptr)

			tt.ptr = uptr
			tt.l = 1
			tt.c = 1
			tt.v = x
			tt.t = Dtype{xt}
			tt.mask = mask
			tt.shape = 

		default:
			panic("Unsupported Tensor Type")
		}
	}
	return f
}

A scalar value should not be masked. Is there a reason why a scalar value is maskable? @kabaka0

There are various instances where retVal = New(FromScalar(argmaxI(t.Ints(), t.mask))) or something similar is called, but I cannot for rhyme or reason figure out why.

Copied from original issue: gorgonia/gorgonia#132

Weird behavior when Iterating over tensors

Whilst working with the tensor.Iterator functionality I found some strange behavior, when using iterator in a for loop like follows:

it = t.Iterator()  // t is a tensor.Dense
for i, err := it.Start(); err == nil; i, err = it.Next() {
	fmt.Printf("i = %v, coord = %v\n", i, it.Coord())
}
  1. When using the iterator as stated above, the 'first' element of the tensor is always last in the iteration. This is not really a big issue if you want to iterate over all elements and order does not matter, but it is weird nonetheless. As an example consider the tensor [1, 2, 3, 4] with shape (2, 2). It will visit the elements in order:
    i = 0, coord = [0 1]
    i = 1, coord = [1 0]
    i = 2, coord = [1 1]
    i = 3, coord = [0 0]  <------------ should be first
    
  2. When iterating over a vector or a scalar, the indices are off by one. Again same tensor, but as a vector (shape (4,)):
    i = 0, coord = [1]
    i = 1, coord = [2]
    i = 2, coord = [3]
    i = 3, coord = [4]  <----------- gives index error when used
    
    The same thing happens with 'scalar-like' tensors, like: [1] with shape (1, 1, 1):
    i = 0, coord = [1 0 0]  <--------- also index error, should be [0 0 0] always for scalars
    
    Interestingly, when the tensor is a vector/scalar View of a tensor that is not a vector/scalar (i.e. obtained by slicing), the issue does not happen.

What I found to work properly is to use the following for loop instead, but I don't think this is the intended way of iterating.

it = t.Iterator()
for it.Reset(); !it.Done(); it.Next() {
	...
}

using big.Int as backing type

Hello all!

I am wondering about the best way to go about using big.Int or big.Float as the backing type for a tensor.

data := make([]*big.Int, 4)
data[0] = big.NewInt(0)
data[1] = big.NewInt(1)
data[2] = big.NewInt(2)
data[3] = big.NewInt(3)

a := tensor.New(tensor.WithShape(2, 2), tensor.WithBacking(data))
b := tensor.New(tensor.WithShape(2, 2), tensor.WithBacking(data))

works fine, but you cannot do

c, err := a.Mul(b)

because the type is not a member of Number.

I browsed the docs for a while and it looks like I could support this by creating an engine that implements Muler. But I would have to do this for a bunch of ops I want, and again for big.Float.

I may be wrong but I feel like I would get a lot of this for free if I could tell tensor how two big.Int's should be multiplied. e.g. return big.NewInt(0).Mul(a,b)

I am fairly new to golang so maybe this is a dumb question, but is there a way to do this?

Is there a more recommended way to go about this?

Thanks!

cannot use b (type BLAS) as type "gorgonia.org/tensor"

i am loading my model and i take this problem

` // Create a backend receiver
backend := gorgonnx.NewGraph()
// Create a model and set the execution backend
model := onnx.NewModel(backend)
// read the onnx model
b, _ := ioutil.ReadFile("REDPropiaFinal.onnx")

err := model.UnmarshalBinary(b)
if err != nil {
	log.Fatal(err)
}`

and I get the following error

vendor/gorgonia.org/gorgonia/blas.go:46:12: cannot use b (type BLAS) as type "gorgonia.org/tensor".BLAS in argument to "gorgonia.org/tensor".Use:
BLAS does not implement "gorgonia.org/tensor".BLAS (missing Caxpy method)

my go.mod file:

module workspace/keras
go 1.16
require (
github.com/apache/arrow/go/arrow v0.0.0-20210716061910-93bdbf1df56f // indirect
github.com/chewxy/math32 v1.0.8 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/owulveryck/onnx-go v0.5.0
gonum.org/v1/gonum v0.9.3 // indirect
gorgonia.org/tensor v0.9.20
)

P.S. Sorry but it`s my first question in github

Multidimensional array assignment

Given an array x of shape (3, 100, 100), does gorgonia/tensor have a way to do

x[0] = y
x[1] = z
x[2] = w

where w, y and z are of shape (100, 100)

Any particular reason why ss and rs are not exported

Hi!
I would like to use this library for tensor manipulations in Golang. Is there any reason as to why range slices and single element slices are not exported in slice.go? It would be very convenient if they were exported, e.g. like this:

func RangeSlice(start, end int, opts ...int) Slice {
...
}
type SS int

Reduction Ops fail

While working on gorgonia/gorgonia#326, I found that calls into the standard engine for max operations were failing if multiple (>2) reduction dimensions were given. I tracked this to a problem in the default engine where axis were tweaked due to prior reductions.

Filing this for tracking purposes. I intend to try and fix it myself.

MaskedTensor has some fundamental arithmetic issues if we follow Numpy's conventions.

From @chewxy on July 9, 2017 22:30

func TestMaskedPlay(t *testing.T) {
	a := New(WithShape(2, 2), WithBacking([]float64{1, 2, 3, 4}), WithMask([]bool{true, false, false, false}))
	t.Logf("a \n%v", a)
	t.Logf("%v | %v", a.mask, a.l)

	b := New(WithShape(2, 2), WithBacking([]float64{1, 2, 3, 4}))
	t.Logf("b \n%v", b)

	incr := New(WithShape(2, 2), WithBacking([]float64{100, 100, 100, 100}))
	StdEng{}.Add(a, b, WithIncr(incr))
	t.Logf("\n%v ", incr)
}

Results in:

	masked_test.go:7: a 
		⎡--   2⎤
		⎣ 3   4⎦
	masked_test.go:8: [true false false false] | 4
	masked_test.go:11: b 
		⎡1  2⎤
		⎣3  4⎦
	masked_test.go:21: 
		⎡100  104⎤
		⎣106  108⎦

By and large this makes sense. However, current tests fail mainly because the original APIs were built to be mirrors of Numpy's API for masked arrays. In Numpy:

>>> import numpy as np
>>> import numpy.ma as ma
>>> y = ma.array([1, 2, 3], mask = [0, 1, 0])

>>> incr = np.array([100,100,100])
>>> x = np.array([2,4,6])
>>> incr += x + y
>>> incr
array([103, 104, 109])
>>> x
array([2, 4, 6])
>>> y
masked_array(data = [1 -- 3],
             mask = [False  True False],
       fill_value = 999999)

>>> incr = np.array([100,100,100])
>>> incr += x * y
>>> incr
array([102, 104, 118])

The last answer is pretty WTF-ing. It violates an arithmetic convention (PEDMAS)

@kabaka0 care to weigh in? What should the correct way be?

Copied from original issue: gorgonia/gorgonia#133

Upgrade assume-no-moving-gc to the latest version.

Hi

We have been using this package in some of our projects. It has been working great so far. Since go 1.18 we have a panic caused by assume-no-moving-gc. This can be fixed by setting the env var : ASSUME_NO_MOVING_GC_UNSAFE_RISK_IT_WITH=go1.18

I updated the version in go.mod and it looks like everything is working. I don't know if this has any other implications. But Would be nice to get rid of the panic and update the package to go 1.18?

I'll try to create a pull request with the changes.

Casting between data types

Is there a way to cast a dense matrix between different data types. The use case is that I have data that's a uint8 but I want to represent it as a dense matrix of float32.

thank you

Panic on Transposed RowVector

[Fix found, pull request sent]
This simple code panics

2020/10/15 13:11:28 (3, 1)
C[-1  %!v(PANIC=Format method: runtime error: index out of range [3] with length 3)
package main

import (
	"log"

	"gorgonia.org/tensor"
)

func main() {
	var T tensor.Tensor
	T = tensor.New(
		tensor.WithShape(1, 3),
		tensor.WithBacking([]float64{-1, 0, 1}))
	T, _ = tensor.T(T)
	log.Printf("%v\n%v\n", T.Shape(), T)
}

README examples don't reflect proper usage

The readme gives the following example:

a := New(WithShape(2, 2), WithBacking([]int{1, 2, 3, 4}))
fmt.Printf("a:\n%v\n", a)

I think this should be:

a := tensor.New(tensor.WithShape(2, 2), tensor.WithBacking([]int{1, 2, 3, 4}))
fmt.Printf("a:\n%v\n", a)

This is how gorgonia uses it currently.
I think the readme was probably using dot imports where the package name wasn't necessary but it is not recommended to do that.

Dot imports. If a program imports a standard package using import . "path", additional names defined in the imported package in future releases may conflict with other names defined in the program. We do not recommend the use of import . outside of tests, and using it may cause a program to fail to compile in future releases.

Slicing should be allowed on contiguous views

Right now this happens

func (t *Dense) Reshape(dims ...int) error {
	if t.viewOf != 0 {
		return errors.Errorf(methodNYI, "Reshape", "views")
	}

	if t.old != nil {
		t.Transpose()
	}

	return t.reshape(dims...)
}

it should be changed to t.viewOf != 0 && ...

Rotating Tensors/Denses, similar to numpy implementation

Numpy has a way to rotate matrices by increments of 90 degrees using their rot90 method that uses transposes and flips.

There is a transpose method for Tensors, but no way of flipping the Tensor that I see.

Is there a way of properly rotating a Tensor/Dense currently, other than through manual iteration over the data? If not, should this be added?

XXXScalar functions do not respect the Dtype of the tensor.

	a := New(WithShape(4, 2), WithBacking([]float64{1, 1, 1, 1, 1, 1, 1, 1}))
	b := New(WithShape(2, 4), WithBacking([]float64{0, 1, 0, 1, 0, 1, 0, 1}))
	c, _ := a.MatMul(b)
	d, err := Div(c, 2) // 2 is an untyped constant that will be used as an int.
	if err == nil {
		log.Fatal("expected an error")
	}

`complex64` and `complex128` are currently excluded in from being generated by `Generate` method for quick check

From @chewxy on August 28, 2017 9:17

What happens for Generate() is that testing/quick will generate really huge complex numbers, such that when it's Pow'd it becomes +Inf+Infi instead of returning the identity.

Example:

var identity complex128 = 1
x := 1.2137443348035267e+308+1.5113294366498325e+308i
y := cmplx.Pow(x, identity)
// returns (+Inf+Infi)

This causes tests and the CI to fail, therefore it's been temporarily removed until we can figure out the right thing to do

Copied from original issue: gorgonia/gorgonia#143

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.