1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Shared input and weight validation for the layer module
//!
//! Every layer checks its own input shapes and its own weight assignments. The checks
//! themselves repeat across layers, so this module holds the shared logic behind a small set of
//! functions:
//!
//! - [`validate_weight_shape`] and [`validate_optional_weight`] guard the setter that assigns a
//! loaded or user-supplied array to a layer, before the array reaches the layer's storage.
//! - [`start_build`] and [`start_build_many`] guard the start of a layer's build step, and
//! refuse a second build for a different input shape.
//! - [`validate_built_input`] guards a forward pass, and checks a live tensor against the shape
//! a layer already built for.
//!
//! Every function here returns a [`crate::error::Error`] on failure, and never panics.
use crateError;
use crateNnError;
use crateShape;
/// Validates that a weight array assigned to a layer has the shape the layer expects
///
/// Loaded or user-supplied weights must match the shape of the stored array.
/// This check reports a shape error before the array enters the layer math
///
/// # Parameters
///
/// - `name` - Name of the parameter being set, used in the error message, such as "kernel" or
/// "bias"
/// - `expected` - Shape the layer currently has for this parameter
/// - `found` - Shape of the array being assigned
///
/// # Errors
///
/// - `Error::NeuralNetwork(NnError::WeightShape)` - If the shapes differ
pub
/// Validates that a supplied optional array agrees with the flag that decides whether the
/// layer holds it
///
/// A layer built with `use_bias` set to false holds no bias, so a caller that supplies one has
/// a value that reaches nothing. A layer built with `use_bias` left true holds a bias, so a
/// caller that supplies none leaves it at whatever it already held. Both cases are mistakes,
/// and this reports each of them by name
///
/// # Parameters
///
/// - `name` - Name of the array, such as `"bias"` or `"gamma"`
/// - `flag` - Name of the configuration flag that decides whether the layer holds the array,
/// such as `"use_bias"`
/// - `holds` - Whether the layer holds the array
/// - `supplied` - The array the caller gave, if any
///
/// # Returns
///
/// - `RustymlResult<Option<T>>` - The supplied array, when the 2 sides agree
///
/// # Errors
///
/// - `Error::InvalidParameter` - The layer holds the array and the caller gave none, or the
/// layer holds no such array and the caller gave one
pub
/// Opens the build of a layer, and refuses a second build for another shape
///
/// Every layer that allocates from its input shape calls this first. The answer says whether
/// the layer must allocate. A layer that is already built for the same input keeps every array
/// it holds, so no weight is ever drawn twice
///
/// The comparison frees the batch axis, because 1 layer serves every batch size
///
/// # Parameters
///
/// - `built` - The shape the layer already built for, or `None` before its first build
/// - `layer` - Layer name, which the message names
/// - `input` - Shape the layer is being built for, batch axis first
///
/// # Returns
///
/// - `Result<Option<Shape>, Error>` - `Some(shape)` to allocate and then record, and `None`
/// when the layer already holds every array for this input
///
/// # Errors
///
/// - `Error::InvalidInput` - If the layer is already built for another input shape
pub
/// Opens the build of a layer with several inputs, and refuses a second build for other shapes
///
/// This is [`start_build`] for a merge layer. Such a layer records 1 shape per input, so the
/// answer carries the whole list. A layer that a graph reaches from several nodes builds once,
/// on its first node, and every later node checks its shapes against that build
///
/// The comparison frees the batch axis of every shape, because 1 layer serves every batch size
///
/// # Parameters
///
/// - `built` - The shapes the layer already built for, or `None` before its first build
/// - `layer` - Layer name, which the message names
/// - `inputs` - 1 shape per input of the layer, batch axis first
///
/// # Returns
///
/// - `Result<Option<Vec<Shape>>, Error>` - `Some(shapes)` to allocate and then record, and
/// `None` when the layer already holds every array for these inputs
///
/// # Errors
///
/// - `Error::InvalidInput` - If the layer is already built for another input count, for another
/// rank, or for another shape. Each message names the 2 sides
pub
/// Checks a live input tensor against the shape the layer was built for
///
/// The rank must match, and every axis after the batch axis that the build shape fixes must
/// match as well. The batch axis is never checked, because 1 layer serves every batch size. An
/// axis that the build shape leaves free is not checked either. No array of the layer depends
/// on such an axis
///
/// This is the whole input check of a built layer. It enforces exactly what the layer reports
/// as its output shape. A summary therefore never names an extent that a forward pass would
/// accept a different value for
///
/// # Parameters
///
/// - `built` - The shape the layer built for, or `None` while it holds none
/// - `layer` - Layer name, which the message names
/// - `actual` - Shape of the tensor that arrived
///
/// # Returns
///
/// - `Result<(), Error>` - `Ok` when the tensor fits the build
///
/// # Errors
///
/// - `Error::NeuralNetwork(NnError::NotBuilt)` - If the layer is not built
/// - `Error::InvalidInput` - If the rank or a fixed axis disagrees with the build shape
pub