Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotating Tensors/Denses, similar to numpy implementation #37

Open
BryceBeagle opened this issue Oct 10, 2018 · 3 comments
Open

Rotating Tensors/Denses, similar to numpy implementation #37

BryceBeagle opened this issue Oct 10, 2018 · 3 comments

Comments

@BryceBeagle
Copy link

BryceBeagle commented Oct 10, 2018

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?

@chewxy
Copy link
Member

chewxy commented Oct 10, 2018

This is what was used for my AlphaGo reimplementation:

func RotateBoard(board []float32, m, n int) ([]float32, error) {
	if m != n {
		return nil, errors.Errorf("Cannot handle m %d, n %d. This function only takes square boards", m, n)
	}
	copied := make([]float32, len(board))
	copy(copied, board)
	it := MakeIterator(copied, m, n)
	for i := 0; i < m/2; i++ {
		mi1 := m - i - 1
		for j := i; j < mi1; j++ {
			mj1 := m - j - 1
			tmp := it[i][j]
			// right to top
			it[i][j] = it[j][mi1]

			// bottom to right
			it[j][mi1] = it[mi1][mj1]

			// left to bottom
			it[mi1][mj1] = it[mj1][i]

			// tmp is left
			it[mj1][i] = tmp
		}
	}
	ReturnIterator(m, n, it)
	return copied, nil
}
func ReturnIterator(m, n int, it [][]float32) {
	if d, ok := iterPool[m]; ok {
		if _, ok := d[n]; ok {
			iterPool[m][n].Put(it)
		} else {
			iterPool[m][n] = &sync.Pool{
				New: func() interface{} {
					retVal := make([][]float32, m)
					for i := range retVal {
						retVal[i] = make([]float32, n)
					}
					return retVal
				},
			}
			iterPool[m][n].Put(it)
		}
	} else {
		iterPool[m] = make(map[int]*sync.Pool)
		iterPool[m][n] = &sync.Pool{
			New: func() interface{} {
				retVal := make([][]float32, m)
				for i := range retVal {
					retVal[i] = make([]float32, n)
				}
				return retVal
			},
		}
		iterPool[m][n].Put(it)
	}
}

func MakeIterator(board []float32, m, n int) (retVal [][]float32) {
	retVal = borrowIterator(m, n)
	for i := range retVal {
		start := i * int(m)
		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&retVal[i]))
		hdr.Data = uintptr(unsafe.Pointer(&board[start]))
		hdr.Len = int(n)
		hdr.Cap = int(n)
	}
	return

}

func borrowIterator(m, n int) [][]float32 {
	if d, ok := iterPool[m]; ok {
		if d2, ok := d[n]; ok {
			return d2.Get().([][]float32)
		}
	}
	retVal := make([][]float32, m)
	for i := range retVal {
		retVal[i] = make([]float32, n)
	}
	return retVal
}

I agree it needs to be added. I don't quite have the bandwidth right now but these two snippets may be useful

@omarsamhan-zz
Copy link

I can take this issue. I am a newbie in terms of open-source collaboration so please bear with me :)

I might start with dense matrices first and then move on to tensors.

The rotate function can be implemented in dense_matop_memmove.go and/or dense_matop.go by allocating a new matrix and/or performing an "in-place" transformation, respectively.

Which implementation should I aim for first?

Note: The use of the term "in-place" is a slight abuse of notation

@chewxy
Copy link
Member

chewxy commented Jun 7, 2020

safety first, so memmove - i.e. allocating a new matrix fist. then a unsafe version (i.e. in place).

Send PR :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants