~/bend-docscommunity

tensor_shape.bend source

tensor_shape.bend on the hub · documented module

# Storage-independent extents. View derives this descriptor from its axes.import Baseimport ./machine_limits.bend as Machineimport ./math_checked_word.bend as CheckedWordtype Shape is Data:  Shape{extents: List<&2,U32>}def rank(shape: Shape) -> Nat:  Shape{extents} = shape  List.length(&2,U32,extents)def extent(shape: Shape,axis: Nat) -> Maybe<&2,U32>:  Shape{extents} = shape  List.get(&2,U32,extents,axis)def same_extents(left: List<&2,U32>,right: List<&2,U32>) -> Bool:  match left right:    case Nil{} Nil{}: True{}    case Con{x,xs} Con{y,ys}: U32.is_eq(x,y) && same_extents(xs,ys)    case left right: False{}def same(left: Shape,right: Shape) -> Bool:  Shape{xs} = left  Shape{ys} = right  same_extents(xs,ys)def has_zero(extents: List<&2,U32>) -> Bool:  match extents:    case Nil{}: False{}    case Con{x,xs}: U32.is_zero(x) || has_zero(xs)def checked_multiply(left: U32,right: U32) -> Maybe<&2,U32>:  CheckedWord.multiply(left,right)def product(extents: List<&2,U32>,accumulator: Maybe<&2,U32>) -> Maybe<&2,U32>:  match extents accumulator:    case Nil{} result: result    case Con{x,xs} Some{value}: product(xs,checked_multiply(value,x))    case Con{x,xs} None{}: None{}def count_nonempty(empty: Bool,extents: List<&2,U32>) -> Maybe<&2,U32>:  match empty:    case True{}: Some{0}    case False{}: product(extents,Some{1})# Scalar [] has one element. Any zero extent makes an empty shape, even when# an earlier partial product would overflow. Nonempty overflow is rejected.def numel(shape: Shape) -> Maybe<&2,U32>:  Shape{+extents} = shape  count_nonempty(has_zero(extents),extents)# NumPy broadcasting of one axis: equal extents, or one of them is 1.def broadcast_extent(same: Bool,left_one: Bool,right_one: Bool,left: U32,right: U32) -> Maybe<&2,U32>:  match same left_one right_one:    case True{} left_one right_one: Some{left}    case False{} True{} right_one: Some{right}    case False{} False{} True{}: Some{left}    case False{} False{} False{}: None{}def prepend_extent(extent: Maybe<&2,U32>,rest: Maybe<&2,List<&2,U32>>) -> Maybe<&2,List<&2,U32>>:  match extent rest:    case Some{extent} Some{rest}: Some{extent <> rest}    case extent rest: None{}def broadcast_same_rank(left: List<&2,U32>,right: List<&2,U32>) -> Maybe<&2,List<&2,U32>>:  match left right:    case Nil{} Nil{}: Some{[]}    case Con{+x,xs} Con{+y,ys}:      prepend_extent(broadcast_extent(U32.is_eq(x,y),U32.is_eq(x,1),U32.is_eq(y,1),x,y),broadcast_same_rank(xs,ys))    case left right: None{}# The extents with leading axes of extent 1 added up to rank.def with_rank(+extents: List<&2,U32>,rank: Nat) -> List<&2,U32>:  List.append(&2,U32,List.replicate(U32,Nat.sub(rank,List.length(&2,U32,extents)),1),extents)# The common extents of two operands under NumPy broadcasting: the extents are# aligned at the last axis, a missing axis counts as 1, and on each axis the# extents are equal or one of them is 1.def broadcast(+left: List<&2,U32>,+right: List<&2,U32>) -> Maybe<&2,List<&2,U32>>:  broadcast_same_rank(with_rank(left,List.length(&2,U32,right)),with_rank(right,List.length(&2,U32,left)))# The extents without one axis.def without_axis(+extents: List<&2,U32>,+axis: Nat) -> List<&2,U32>:  List.append(&2,U32,List.take(&2,U32,extents,axis),List.drop(&2,U32,extents,1n+axis))def sum_when(valid: Bool,value: U32) -> Maybe<&2,U32>:  match valid:    case True{}: Some{value}    case False{}: None{}def checked_add(+left: U32,+right: U32) -> Maybe<&2,U32>:  sum_when(U32.is_le(right,(4294967295 - left : U32)),(left + right : U32))def checked_add_to(left: Maybe<&2,U32>,right: U32) -> Maybe<&2,U32>:  match left:    case Some{left}: checked_add(left,right)    case None{}: None{}# Each extent with before elements added in front and after elements behind.# The three lists have one entry per axis and no sum may wrap.def padded(extents: List<&2,U32>,before: List<&2,U32>,after: List<&2,U32>) -> Maybe<&2,List<&2,U32>>:  match extents before after:    case Nil{} Nil{} Nil{}: Some{[]}    case Con{extent,extents} Con{front,before} Con{back,after}:      prepend_extent(checked_add_to(checked_add(front,extent),back),padded(extents,before,after))    case extents before after: None{}