tensor_window.bend source
tensor_window.bend on the hub · documented module
# Two-dimensional pooling over the last two axes, as two ordered sweeps. Along# the columns, each row of the window folds its k samples in increasing kx# from pad: line = op(...op(op(pad, x0), x1)..., x(k-1)). Along the rows, the# output folds the k lines in increasing ky from pad. A sample outside the# input reads pad, so pad should be the identity of the operation.import Baseimport ./storage_buffer.bend as Storageimport ./tensor_shape.bend as Shapesimport ./tensor_access.bend as Accessimport ./tensor_operations.bend as Operationsimport ./tensor.bend as Tensorsimport ./tensor_layout.bend as Layoutimport ./tensor_band.bend as Bandsimport ./tensor_band_window.bend as BandWindowimport ./tensor_band_pool.bend as BandPooldef out_extent(+extent: U32,+kernel: U32,+stride: U32,+padding: U32) -> U32: ((extent + 2 * padding - kernel : U32) / U32.max(stride,1) + 1 : U32)def geometry(+shape: List<&2,U32>,+kernel: U32,+stride: U32,+padding: U32) -> Access.Pool: +rank = List.length(&2,U32,shape) +height = Layout.extent_at(shape,Nat.sub(rank,2n)) +width = Layout.extent_at(shape,Nat.sub(rank,1n)) Access.Pool{height,width,kernel,stride,padding,out_extent(height,kernel,stride,padding),out_extent(width,kernel,stride,padding)}def leading(ranked: Bool,+shape: List<&2,U32>) -> Maybe<&2,U32>: match ranked: case True{}: Shapes.product(List.take(&2,U32,shape,Nat.sub(List.length(&2,U32,shape),2n)),Some{1}) case False{}: None{}# The number of planes before the last two axes; none below rank two.def planes(+shape: List<&2,U32>) -> Maybe<&2,U32>: leading(Nat.is_le(2n,List.length(&2,U32,shape)),shape)def pooled(~Element: Data,name: String,+shape: List<&2,U32>,+out_shape: List<&2,U32>,fill: Element,+workers: U32, result: Operations.Outcome<(Storage.Buffer<Element> & Storage.Buffer<Element>)>) -> Tensors.Tensor<Element>: match result: case Operations.Accepted{(input,output)}: Tensors.Tensor{output,out_shape,fill,Tensors.Usable{},workers} case Operations.Rejected{(input,output)}: Tensors.Tensor{output,out_shape,fill,Tensors.Failed{Tensors.Problem{name,Tensors.InvalidArgument{shape}}},workers}def pool_middle(~Element: Data,~operation: Element -> Element -> Element,name: String,+pad: Element,middle: Maybe<&2,U32>,count: U32,+channels: U32,+pool: Access.Pool, storage: Storage.Buffer<Element>,+shape: List<&2,U32>,+out_shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: match middle: case Some{middle}: pooled(~Element,name,shape,out_shape,fill,workers, Operations.pool(~Element,~operation,pad,storage,pool,channels,Tensors.buffer(~Element,fill,middle),Tensors.buffer(~Element,fill,count))) case None{}: Tensors.failed(~Element,name,Tensors.InvalidArgument{shape},shape,fill,workers)# The column-swept planes keep the rows of the input and have the columns of# the output.def middle_shape(+shape: List<&2,U32>,pool: Access.Pool) -> List<&2,U32>: Access.Pool{height,width,kernel,stride,padding,out_height,out_width} = pool List.append(&2,U32,List.take(&2,U32,shape,Nat.sub(List.length(&2,U32,shape),2n)),[height,out_width])def pool_counts(~Element: Data,~operation: Element -> Element -> Element,name: String,+pad: Element,channels: Maybe<&2,U32>,count: Maybe<&2,U32>,+pool: Access.Pool, storage: Storage.Buffer<Element>,+shape: List<&2,U32>,+out_shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: match channels count: case Some{+channels} Some{count}: pool_middle(~Element,~operation,name,pad,Tensors.count_of(middle_shape(shape,pool)),count,channels,pool,storage,shape,out_shape,fill,workers) case channels count: Tensors.failed(~Element,name,Tensors.InvalidArgument{shape},shape,fill,workers)def pool_planes(~Element: Data,~operation: Element -> Element -> Element,name: String,+pad: Element,channels: Maybe<&2,U32>,+pool: Access.Pool, storage: Storage.Buffer<Element>,+shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: Access.Pool{height,width,kernel,stride,padding,+out_height,+out_width} = pool +out_shape = List.append(&2,U32,List.take(&2,U32,shape,Nat.sub(List.length(&2,U32,shape),2n)),[out_height,out_width]) pool_counts(~Element,~operation,name,pad,channels,Tensors.count_of(out_shape),pool,storage,shape,out_shape,fill,workers)def pool_status(~Element: Data,~operation: Element -> Element -> Element,name: String,status: Tensors.Status,+pad: Element,+kernel: U32,+stride: U32,+padding: U32, storage: Storage.Buffer<Element>,+shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: match status: case Tensors.Usable{}: pool_planes(~Element,~operation,name,pad,planes(shape),geometry(shape,kernel,stride,padding),storage,shape,fill,workers) case Tensors.Failed{problem}: Tensors.Tensor{storage,shape,fill,Tensors.Failed{problem},workers}# pad is both the value outside the input and the initial accumulator.def band_shape(shape: List<&2,U32>,geometry: Bands.Geometry) -> Bool: match shape geometry: case [c,h,w] Bands.Geometry{n,ci,hi,wi}: U32.is_eq(n,1) && U32.is_eq(c,ci) && U32.is_eq(h,hi) && U32.is_eq(w,wi) case [n,c,h,w] Bands.Geometry{ni,ci,hi,wi}: U32.is_eq(n,ni) && U32.is_eq(c,ci) && U32.is_eq(h,hi) && U32.is_eq(w,wi) case shape geometry: False{}def band_execute(~Element: Data,~operation: Element -> Element -> Element,+pad: Element,pool: Access.Pool,+geometry: Bands.Geometry,bands: Bands.Bands(~Element), out_shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: match pool geometry: case Access.Pool{height,width,kernel,stride,padding,+oh,+ow} Bands.Geometry{batch,channels,ih,iw}: Tensors.Banded{BandPool.execute(~Element,~operation,BandPool.Context{BandWindow.Geometry{geometry,kernel,stride,padding,oh,ow},pad,fill,workers},bands), Bands.Geometry{batch,channels,oh,ow},out_shape,fill,Tensors.Usable{},workers}# The band route needs the same counts as the dense route: the planes swept# along the columns and the output.def band_count(~Element: Data,~operation: Element -> Element -> Element,name: String,valid: Bool,planes: Maybe<&2,U32>,count: Maybe<&2,U32>,+pad: Element,+pool: Access.Pool,+geometry: Bands.Geometry,bands: Bands.Bands(~Element), +shape: List<&2,U32>,out_shape: List<&2,U32>,+fill: Element,+workers: U32) -> Tensors.Tensor<Element>: match valid planes count pool: case True{} Some{planes} Some{count} pool: band_execute(~Element,~operation,pad,pool,geometry,bands,out_shape,fill,workers) case valid planes count Access.Pool{height,width,kernel,stride,padding,oh,ow}: Tensors.with_dense(~Element,~Tensors.Tensor<Element>,Tensors.Banded{bands,geometry,shape,fill,Tensors.Usable{},workers},storage => shape => fill => status => workers => pool_status(~Element,~operation,name,status,pad,kernel,stride,padding,storage,shape,fill,workers))def band_geometry(~Element: Data,~operation: Element -> Element -> Element,name: String,pad: Element,+pool: Access.Pool,+geometry: Bands.Geometry,bands: Bands.Bands(~Element), +shape: List<&2,U32>,fill: Element,workers: U32) -> Tensors.Tensor<Element>: Access.Pool{+height,+width,+kernel,+stride,+padding,+out_height,+out_width} = pool +pool = {Access.Pool{height,width,kernel,stride,padding,out_height,out_width} : Access.Pool} +out_shape = List.append(&2,U32,List.take(&2,U32,shape,Nat.sub(List.length(&2,U32,shape),2n)),[out_height,out_width]) band_count(~Element,~operation,name,band_shape(shape,geometry) && Operations.pool_geometry(pool) && U32.is_lt(0,height) && U32.is_lt(0,width) && Bool.not(Shapes.has_zero(out_shape)),Tensors.count_of(middle_shape(shape,pool)),Tensors.count_of(out_shape),pad,pool,geometry,bands,shape,out_shape,fill,workers)def band_status(~Element: Data,~operation: Element -> Element -> Element,name: String,status: Tensors.Status,pad: Element,+kernel: U32,+stride: U32,+padding: U32, layout: Bands.Geometry,bands: Bands.Bands(~Element),+shape: List<&2,U32>,fill: Element,workers: U32) -> Tensors.Tensor<Element>: match status: case Tensors.Failed{+problem}: Tensors.with_dense(~Element,~Tensors.Tensor<Element>,Tensors.Banded{bands,layout,shape,fill,Tensors.Failed{problem},workers}, storage => shape => fill => status => workers => pool_status(~Element,~operation,name,status,pad,kernel,stride,padding,storage,shape,fill,workers)) case Tensors.Usable{}: band_geometry(~Element,~operation,name,pad,geometry(shape,kernel,stride,padding),layout,bands,shape,fill,workers)def pool2d(~Element: Data,~operation: Element -> Element -> Element,name: String,+pad: Element,tensor: Tensors.Tensor<Element>, +kernel: U32,+stride: U32,+padding: U32) -> Tensors.Tensor<Element>: match tensor: case Tensors.Tensor{storage,shape,fill,status,workers}: pool_status(~Element,~operation,name,status,pad,kernel,stride,padding,storage,shape,fill,workers) case Tensors.Banded{bands,layout,shape,fill,status,workers}: band_status(~Element,~operation,name,status,pad,kernel,stride,padding,layout,bands,shape,fill,workers)