tensor icon indicating copy to clipboard operation
tensor copied to clipboard

Rotating Tensors/Denses, similar to numpy implementation

Open BryceBeagle opened this issue 7 years ago • 3 comments

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?

BryceBeagle avatar Oct 10 '18 20:10 BryceBeagle

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

chewxy avatar Oct 10 '18 20:10 chewxy

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

omarsamhan-zz avatar Jun 07 '20 13:06 omarsamhan-zz

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

Send PR :)

chewxy avatar Jun 07 '20 21:06 chewxy