src/autograd/sum_dims.bend
bendtorch-core@0.1.0.0 · 160 lines · 22 definitions
1import Base
2import bendtorch-core@0.1.0.0/src/shape/core.bend as Shape
3import bendtorch-core@0.1.0.0/src/tensor/core.bend as Core
4import bendtorch-core@0.1.0.0/src/tensor/layout.bend as Layout
5import bendtorch-core@0.1.0.0/src/tensor/transforms.bend as Transforms
6import bendtorch-core@0.1.0.0/src/autograd/reduce.bend as Reduce
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21type Error is Data:
22 ReduceError{error: Reduce.Error}
23 TransformError{error: Transforms.Error}
24
25
26type Plan is Data:
27 Plan{kept: Shape.Shape, dropped: List<&2, U32>}
28
29
30def reduces(+axes: List<&2, U32>, +axis: U32) -> Bool:
31 match axes:
32 case Nil{}:
33 True{}
34 case +head <> +tail:
35 Layout.contains(axis, head <> tail)
36
37
38def kept_from(+axes: List<&2, U32>, dimensions: List<&2, U32>, +axis: U32) -> List<&2, U32>:
39 match dimensions:
40 case Nil{}:
41 Nil{}
42 case extent <> rest:
43 Bool.pick(U32, reduces(axes, axis), 1, extent) <> kept_from(axes, rest, U32.add(axis, 1))
44
45def kept_dimensions(+axes: List<&2, U32>, dimensions: List<&2, U32>) -> List<&2, U32>:
46 kept_from(axes, dimensions, 0)
47
48def dropped_next(reduced: Bool, extent: U32, rest: List<&2, U32>) -> List<&2, U32>:
49 match reduced:
50 case True{}:
51 rest
52 case False{}:
53 extent <> rest
54
55
56def dropped_from(+axes: List<&2, U32>, dimensions: List<&2, U32>, +axis: U32) -> List<&2, U32>:
57 match dimensions:
58 case Nil{}:
59 Nil{}
60 case extent <> rest:
61 dropped_next(reduces(axes, axis), extent, dropped_from(axes, rest, U32.add(axis, 1)))
62
63def dropped_dimensions(+axes: List<&2, U32>, dimensions: List<&2, U32>) -> List<&2, U32>:
64 dropped_from(axes, dimensions, 0)
65
66
67
68def limit(dimensions: List<&2, U32>) -> U32:
69 match dimensions:
70 case Nil{}:
71 1
72 case +extent <> +rest:
73 U32.from_nat(Shape.rank_dimensions(extent <> rest))
74
75
76
77def refused(bounded: Bool, unique: Bool) -> Maybe<&2, Layout.Error>:
78 match bounded unique:
79 case False{} unique:
80 Some{Layout.AxisOutOfBounds{}}
81 case True{} False{}:
82 Some{Layout.DuplicateAxis{}}
83 case True{} True{}:
84 None{}
85
86def refusal(+axes: List<&2, U32>, +limit: U32) -> Maybe<&2, Layout.Error>:
87 refused(Layout.bounded(axes, limit), Layout.unique(axes))
88
89def plan_built(-kept: List<&2, U32>, +dropped: List<&2, U32>, built: Shape.ConstructResult<kept>) -> Result<&2, &2, Error, Plan>:
90 match built:
91 case Shape.SError{error}:
92 Fail{ReduceError{Reduce.TensorError{Core.ShapeError{error}}}}
93 case Shape.SBuilt{+shape, preserved}:
94 Done{Plan{shape, dropped}}
95
96def plan_checked(+axes: List<&2, U32>, +dimensions: List<&2, U32>, checked: Maybe<&2, Layout.Error>) -> Result<&2, &2, Error, Plan>:
97 match checked:
98 case Some{error}:
99 Fail{TransformError{Transforms.LayoutError{error}}}
100 case None{}:
101 plan_built(kept_dimensions(axes, dimensions), dropped_dimensions(axes, dimensions),
102 Shape.construct(kept_dimensions(axes, dimensions)))
103
104
105def plan(+shape: Shape.Shape, +axes: List<&2, U32>) -> Result<&2, &2, Error, Plan>:
106 plan_checked(axes, Shape.dimensions(shape), refusal(axes, limit(Shape.dimensions(shape))))
107
108def transformed(result: Result<&2, &1, Transforms.Error, Core.Dynamic>) -> Result<&2, &1, Error, Core.Dynamic>:
109 match result:
110 case Fail{error}:
111 Fail{TransformError{error}}
112 case Done{output}:
113 Done{output}
114
115def shaped(+dropped: List<&2, U32>, keepdim: Bool, output: Core.Dynamic) -> Result<&2, &1, Error, Core.Dynamic>:
116 match keepdim:
117 case True{}:
118 Done{output}
119 case False{}:
120 transformed(Transforms.reshape_dynamic(output, dropped))
121
122def squeezed(+dropped: List<&2, U32>, keepdim: Bool, result: Result<&2, &1, Reduce.Error, Core.Dynamic>) ->
123 Result<&2, &1, Error, Core.Dynamic>:
124 match result:
125 case Fail{error}:
126 Fail{ReduceError{error}}
127 case Done{output}:
128 shaped(dropped, keepdim, output)
129
130def summed(+dropped: List<&2, U32>, keepdim: Bool, pair: Core.Dynamic & Result<&2, &1, Reduce.Error, Core.Dynamic>) ->
131 Core.Dynamic & Result<&2, &1, Error, Core.Dynamic>:
132 (input, result) = pair
133 (input, squeezed(dropped, keepdim, result))
134
135
136
137def sum_planned(input: Core.Dynamic, plan: Plan, keepdim: Bool) -> Core.Dynamic & Result<&2, &1, Error, Core.Dynamic>:
138 Plan{+kept, +dropped} = plan
139 summed(dropped, keepdim, Reduce.reduce_to(input, kept))
140
141def sum_checked(input: Core.Dynamic, keepdim: Bool, planned: Result<&2, &2, Error, Plan>) ->
142 Core.Dynamic & Result<&2, &1, Error, Core.Dynamic>:
143 match planned:
144 case Fail{error}:
145 (input, Fail{error})
146 case Done{plan}:
147 sum_planned(input, plan, keepdim)
148
149
150
151def sum_dims(input: Core.Dynamic, +axes: List<&2, U32>, keepdim: Bool) -> Core.Dynamic & Result<&2, &1, Error, Core.Dynamic>:
152 Core.Packed{+shape, tensor} = input
153 sum_checked(Core.Packed{shape, tensor}, keepdim, plan(shape, axes))
154
155def error_code(error: Error) -> U32:
156 match error:
157 case ReduceError{error}:
158 Reduce.error_code(error)
159 case TransformError{error}:
160 Transforms.error_code(error)