The Go Playground Imports embed Hello, playground Tests Multiple files Display image Sleep Clear

package main import ( "fmt" ) type Tensor interface { Slice(i rng, j ...rng) (p *pmatrix) SlicefromView(p *pmatrix, i rng, j ...rng) } type matrix struct { data []float64 r, c int } // pMatrix is a proxy matrix to do things like extracting a submatrix from matrix using indices type pmatrix struct { m *matrix r, c int ind []int } // For demo purposes, I'm using 2d matrix, actual implementation is for 3d matrices. func Matrix(r, c int, v []float64) (m *matrix) { m = &matrix{ r: r, c: c, data: make([]float64, r*c), } if v != nil { copy(m.data, v) } return } // View creates a proxy matrix of size (pr,pc) of the input matrix 'm', for viewing // the matrix 'm' indirectly. func View(m *matrix, pr, pc int) *pmatrix { return &pmatrix{m, pr, pc, make([]int, pr*pc)} } // Slice works very similarly to Matlab's colon operator. It slices a matrix according to // the specified ranges. Any modifications to the matrix-slice will be seen in the original matrix. // Eg: Let 'm' be a 3x4 matrix. // m = [1 2 3 4; 5 6 7 8; 9 10 11 12] // Then, m.Slice(Rng(0,2),Rng(0,1)) will return a slice of 'm' such that a 3x2 matrix starting at (0,0) is returned, equivalent to Matlab's m(1:3,1:2) // However, for m.Slice(Rng(0,2)), the range will be interpreted as linear // indices and the 1st 3 elements of m, i.e., - [1 2 3] will be returned. func (m *matrix) Slice(i rng, j ...rng) (p *pmatrix) { if i == nil { fmt.Println("matrix.Slice: i: ", i) panic("at least 1 range must be provided") } switch len(j) { case 0: p = &pmatrix{m, 1, len(i), make([]int, len(i))} copy(p.ind, i) if m.c == 1 { p.r, p.c = p.c, p.r } case 1: if m.r == 1 || m.c == 1 { panic("index ranges exceed matrix dimensions. for vectors only " + "provide a single range. Eg: m.Slice(Rng(1,3))") } p = &pmatrix{m, len(i), len(j[0]), make([]int, len(i)*len(j[0]))} x := 0 for _, ix := range i { for _, jx := range j[0] { p.ind[x] = jx + ix*m.c x++ } } default: fmt.Println("matrix.Slice: j: ", j) panic("invalid no. of ranges provided. cannot exceed 1") } return } // SlicefromView slices matrix 'm' using a pre-allocated view of the matrix 'm'. This way we avoid // allocations when slicing. func (m *matrix) SlicefromView(p *pmatrix, i rng, j ...rng) { if i == nil { fmt.Println("matrix.Slice: i: ", i) panic("at least 1 range must be provided") } switch len(j) { case 0: copy(p.ind, i) case 1: jp := j[0] x := 0 for _, ix := range i { for _, jx := range jp { p.ind[x] = jx + ix*m.c x++ } } default: fmt.Println("matrix.Slice: j: ", j) panic("invalid no. of ranges provided. cannot exceed 1") } } func (m *matrix) String() string { s := "" for i := 0; i < m.r; i++ { s += "[" for j := 0; j < m.c; j++ { s += fmt.Sprintf(" %.2e ", m.data[j+m.c*i]) } s += "]

" } return s } func (p *pmatrix) String() string { ix := 0 s := "" for i := 0; i < p.r; i++ { s += "[" for j := 0; j < p.c; j++ { s += fmt.Sprintf(" %.2e ", p.m.data[p.ind[ix]]) ix++ } s += "]

" } return s } func main() { m := Matrix(3, 4, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}) fmt.Println(m) p := m.Slice(Rng(0), Rng(1, 2)) // allocates memory for proxy fmt.Println(p) q := View(m, 2, 3) // preallocate proxy -> caveat is user must know about the proxy implementation, it'd be ideal if that was not the case m.SlicefromView(q, Rng(1, 2), Rng(1, 3)) fmt.Println(q) } type rng []int func compute_rng(beg, inc, end int) (r rng) { if beg <= end && inc > 0 { n := ((end - beg) / inc) + 1 r = make([]int, n) r[0] = beg for i := 1; i < n; i++ { r[i] = r[i-1] + inc } return r } panic("invalid range parameters provided") } func compute_rng_buf(buf []int, beg, inc, end int) { if beg <= end && inc > 0 { buf[0] = beg for i := 1; i < len(buf); i++ { buf[i] = buf[i-1] + inc } return } panic("invalid range parameters provided") } // Rng is used to construct an arithmetic sequence of integers from an inclusive range [beg, inc, end]. // inc and end are optional arguments specified by 'r', whose format is interpreted as [inc, end]. // If the length of 'r' is 0, then the range is interpreted as [beg, 1, beg] // If the length of 'r' is 1, then the range is interpreted as [beg, 1, r[0]] // If the length of 'r' is 2, then the range is interpreted as [beg, r[0], r[1]] func Rng(beg int, r ...int) rng { inc, end := 1, beg switch len(r) { case 0: return rng{beg} case 1: end = r[0] case 2: inc = r[0] end = r[1] default: fmt.Println("Rng: r: ", r) panic("len(r) cannot exceed 2.") } return compute_rng(beg, inc, end) } func RngBuf(buf []int, beg int, r ...int) { inc, end := 1, beg switch len(r) { case 0: buf[0] = beg return case 1: end = r[0] case 2: inc = r[0] end = r[1] default: fmt.Println("Rng: r: ", r) panic("len(r) cannot exceed 2.") } compute_rng_buf(buf, beg, inc, end) }