src/autograd/gradient.bend
bendtorch-core@0.1.0.0 · 150 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/storage/main.bend as Storage
4import bendtorch-core@0.1.0.0/src/tensor/core.bend as Core
5import bendtorch-core@0.1.0.0/src/tensor/layout.bend as Layout
6
7
8
9
10
11
12
13
14
15
16type Error is Data:
17 TensorError{error: Core.Error}
18 ShapeMismatch{}
19
20type Slot is Type:
21 Unset{}
22 Held{gradient: Core.Dynamic}
23
24type Outcome is Type:
25 Accumulated{slot: Slot}
26 Refused{slot: Slot, contribution: Core.Dynamic, error: Error}
27
28type State<-shape: Shape.Shape> is Type:
29 State{contribution: Core.Dynamic, total: Core.Tensor<shape>}
30
31type Step<-shape: Shape.Shape> is Type:
32 Continue{state: State<shape>}
33 Stop{contribution: Core.Dynamic, total: Core.Tensor<shape>, error: Core.Error}
34
35def step_write(-shape: Shape.Shape, contribution: Core.Dynamic,
36 pair: Core.Tensor<shape> & Result<&2, &2, Core.Error, Unit>) -> Step<shape>:
37 (total, result) = pair
38 match result:
39 case Fail{error}:
40 Stop{contribution, total, error}
41 case Done{unit}:
42 Continue{State{contribution, total}}
43
44def step_total(-shape: Shape.Shape, +index: U32, contribution: Core.Dynamic, value: F32,
45 pair: Core.Tensor<shape> & Result<&2, &2, Core.Error, F32>) -> Step<shape>:
46 (total, result) = pair
47 match result:
48 case Fail{error}:
49 Stop{contribution, total, error}
50 case Done{+accumulated}:
51 step_write(shape, contribution, Core.write_flat(shape, total, index, F32.add(accumulated, value)))
52
53def step_contribution(-shape: Shape.Shape, +index: U32, total: Core.Tensor<shape>,
54 pair: Core.Dynamic & Result<&2, &2, Core.Error, F32>) -> Step<shape>:
55 (contribution, result) = pair
56 match result:
57 case Fail{error}:
58 Stop{contribution, total, error}
59 case Done{value}:
60 step_total(shape, index, contribution, value, Core.read_flat(shape, total, index))
61
62def step(-shape: Shape.Shape, +index: U32, state: State<shape>) -> Step<shape>:
63 State{contribution, total} = state
64 step_contribution(shape, index, total, Core.read_dynamic(contribution, index))
65
66def continue_step(+shape: Shape.Shape, rec: State<shape> -> Outcome, result: Step<shape>) -> Outcome:
67 match result:
68 case Stop{contribution, total, error}:
69 Refused{Held{Core.Packed{shape, total}}, contribution, TensorError{error}}
70 case Continue{state}:
71 rec(state)
72
73def conversion_error(+shape: Shape.Shape, state: State<shape>) -> Outcome:
74 State{contribution, total} = state
75 Refused{Held{Core.Packed{shape, total}}, contribution, TensorError{Core.StorageError{Storage.InvalidIndex{}}}}
76
77def decided(+shape: Shape.Shape, +cursor: Nat, +index: U32, state: State<shape>, rec: State<shape> -> Outcome,
78 decision: Shape.BoolCase<Nat.is_eq(U32.to_nat(index), cursor)>) -> Outcome:
79 match decision:
80 case Shape.BFalse{equal}:
81 conversion_error(shape, state)
82 case Shape.BTrue{equal}:
83 continue_step(shape, rec, step(shape, index, state))
84
85def loop(fuel: Nat, +shape: Shape.Shape, +cursor: Nat, state: State<shape>) -> Outcome:
86 match fuel:
87 case 0n:
88 State{contribution, total} = state
89 Accumulated{Held{Core.Packed{shape, total}}}
90 case 1n+remaining:
91 +index = U32.from_nat(cursor)
92 decided(shape, cursor, index, state, state => loop(remaining, shape, 1n+cursor, state),
93 Shape.bool_case(Nat.is_eq(U32.to_nat(index), cursor)))
94
95def held_checked(contribution: Core.Dynamic, +total_shape: Shape.Shape, total: Core.Tensor<total_shape>, same: Bool) -> Outcome:
96 match same:
97 case False{}:
98 Refused{Held{Core.Packed{total_shape, total}}, contribution, ShapeMismatch{}}
99 case True{}:
100 loop(Shape.elements(total_shape), total_shape, 0n, State{contribution, total})
101
102def held(+shape: Shape.Shape, contribution: Core.Dynamic, gradient: Core.Dynamic) -> Outcome:
103 Core.Packed{+total_shape, total} = gradient
104 held_checked(contribution, total_shape, total, Layout.words_same(Shape.dimensions(total_shape), Shape.dimensions(shape)))
105
106def agreed(+shape: Shape.Shape, slot: Slot, contribution: Core.Dynamic) -> Outcome:
107 match slot:
108 case Unset{}:
109 Accumulated{Held{contribution}}
110 case Held{gradient}:
111 held(shape, contribution, gradient)
112
113def checked(+shape: Shape.Shape, slot: Slot, +contribution_shape: Shape.Shape, tensor: Core.Tensor<contribution_shape>, same: Bool) -> Outcome:
114 match same:
115 case False{}:
116 Refused{slot, Core.Packed{contribution_shape, tensor}, ShapeMismatch{}}
117 case True{}:
118 agreed(shape, slot, Core.Packed{contribution_shape, tensor})
119
120def accumulate(+shape: Shape.Shape, slot: Slot, contribution: Core.Dynamic) -> Outcome:
121 Core.Packed{+contribution_shape, tensor} = contribution
122 checked(shape, slot, contribution_shape, tensor,
123 Layout.words_same(Shape.dimensions(contribution_shape), Shape.dimensions(shape)))
124
125
126def grad(slot: Slot) -> Maybe<&1, Core.Dynamic>:
127 match slot:
128 case Unset{}:
129 None{}
130 case Held{gradient}:
131 Some{gradient}
132
133
134def reset(slot: Slot) -> Slot:
135 Unset{}
136
137
138def gradient(+shape: Shape.Shape, slot: Slot) -> Result<&2, &1, Core.Error, Core.Dynamic>:
139 match slot:
140 case Unset{}:
141 Core.construct_values(shape, Core.new(shape, 0.0))
142 case Held{gradient}:
143 Done{gradient}
144
145def error_code(error: Error) -> U32:
146 match error:
147 case TensorError{error}:
148 Core.error_code(error)
149 case ShapeMismatch{}:
150 102